Repository files navigation

#KVMapper

A Library to map one NSDictionary to another that can transform BOTH keys and values with a single map. It also allows you to decouple any formatting you may have to do from one representation of data to another (eg. rest api -> internal model representation).

#####sidenote In trying to allow for as much customisation as possible, this tiny library makes very little assumptions which means you must be somewhat verbose. But see convenience methods that should greatly reduce code for the simpler cases.

##Installation

###cocoapods

add to your podfile:

Pod 'KVMapper' '~>0.3'

###manual

(pro-tip use cocoapods)

Drag contents of KVMapper folder to Xcode your favourite IDE.

##Usage

###instant attention grabbing example

You have a dictionary from a JSON Rest API that gives you a key-value store of cat names and their ages: the NSDictionary representaion looks like this:

NSDictionary *catsFromAPI=@{
@"maggy_the_ninja": @5,
@"magrat_the_rockstar": @6,
@"mable_the_wizard": @2,
… 15 or so more cats
@"milly_the_scratcher":@"3"
};

you want this in an NSDictionary which looks like this:

//llamaCaseCatNames
//ages are now in human years (sort of)
NSDictionary *catsForMyModel=@{
@"maggyTheNinja": @30,
@"magratTheRockstar": @36,
@"mableTheWizard": @12,
… 15 or so more cats
@"millyTheScratcher":@"3"
};

here's how you can do that:

 #import "KVMapper.h"
#import "Transformers.h" //see apendix, used to do snake_case to llameCase transformation
NSDictionary *catsMappingDict=[KVMapper mappingDictionaryForKeys:catsFromAPI.allKeys
defaultKeyTransformer:^NSString *(NSString *catName) {
return [Transformers snakeToLlamaCase:catName];
}
defaultValueTransformer:^id(NSNumber *catAge) {
float humanAge=catAge.floatValue+5;
return [NSNumber numberWithFloat:humanAge];
}];
NSDictionary *catsForMyModel=[KVMapper mappedDictionaryWithInputDictionary:catsFromAPI mappingDictionary:catsMappingDict];
//catsForMyModel is now as in the form @{@"maggyTheNinja": @30, …… etc.}

###Creating a dictionary

To create a dictionary from a dictionary, you need mappedDictionaryWithInputDictionary:mappingDictionary:

verbosely:

+(NSDictionary *)mappedDictionaryWithInputDictionary:(NSDictionary *)inputDict mappingDictionary:(NSDictionary *)mappingDict;
NSDictionary *myMappedDictionary=[KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];

This takes your input dictionary and a mapping dictionary.

####mapping dictionary

A mapping dictionary is an NSDictionary whose keys correspond to the keys of the input dictionary and the values are instances of KVMap.

#####KVMap

the KVMap object is just an NSObject with two properties:

  1. keyTransformationBlock
  2. valueTransformationBlock

The former receives and returns an NSString (the key) the latter receives and return id ie any object you want (the value).

######creating a KVMap

making a KVMap is a simple as setting the key and value transformation blocks:

KVMap *myKVMap=[[KVMap alloc] init];
myKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputString lowercaseString];
};
myKVMap.valueTransformationBlock=^(id *input){
return @5;
};

there is an initaliser initWithKeyTransformationBlock:valueTransformationBlock: to create one in a single step.

####creating a mapping dictionary

To create a mapping dictionary you must create the various KVMaps you need to map the input dictionary's key-value pairs into the form you want. Then set these as values to the key correspondong to the input dictionary's key:

// the input dictionary
NSDictionary *inputDict=@{
@"KEYONE":@1,
@"KEYTWO":@2,
@"myKEYTHREE":@"3",
};
//set up maps (you can use the same map if it applies to multiple KV pairs)
KVMap *myKeyOneAndTwoKVMap=[[KVMap alloc] init];
myKeyOneAndTwoKVMap.keyTransformationBlock=^(NSString *inputString){
return [NSSString stringWithFormat:@"my%@",[inputSting lowerCaseString]];
};
myKeyOneAndTwoKVMap.valueTransformationBlock=^(id *input){
return [NSString stringWithFormat:@"%i",input];
};
KVMap *myKeyThreeKVMap=[[KVMap alloc] init];
myKeyThreeKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputSting lowerCaseString];
};
//create mapping dict
NSDictionary *mappingDict=@{
@"KEYONE":myKeyOneAndTwoKVMap,
@"KEYTWO":myKeyOneAndTwoKVMap,
@"myKEYTHREE":myKeyThreeKVMap
};
// [KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];
// will return:
// @{
// @"mykeyone":@"1",
// @"mykeytwo":@"2",
// @"mykeythree":@"3"
// };

###Convenience Methods

Writing out all the mapping logic can quickly become tedious, especially when we often have to do the same transformation for a key, value or both.

so KVMapper has: KVMapsForKeys:defaultKeyTransformer: and KVMapsForKeys:defaultKeyTransformer:defaultValueTransformer:

so suppose we want to create a mappingDictionary for where all the keys are transformed to llamaCase and all the values (which we get as strings) are NSURLs.

 NSDictionary *mappingDict=[KVMapper mappingDictionaryForKeys:testDict.allKeys
defaultKeyTransformer:^(NSString *inKey){
return [Transformers snakeToLlamaCase:inKey];
}
defaultValueTransformer:^(id inValue){
NSURL *url=[NSURL URLWithString:inValue];
return url;
}];

This mappingDict can now be used in

##Appendix

###The random transformer class

//Transformers.h
#import <Foundation/Foundation.h>
@interface Transformers : NSObject
+(NSString *)snakeToLlamaCase:(NSString *)inKey;
@end
//Transformers.m
#import "Transformers.h"
@implementation Transformers
#pragma mark -string transformers
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"couldn't set value: %@ for undefkey:%@",[value description],key);
}
+(NSString *)snakeToLlamaCase:(NSString *)inKey{
NSArray *keyComponents=[inKey componentsSeparatedByString:@"_"];
return [self llamaCasFromComponents:keyComponents];
}
+(NSString *)llamaCasFromComponents:(NSArray *)components{
NSMutableString *llamaCaseString=[NSMutableString string];
for (NSString *component in components) {
NSString *firstLetterUpper=[[component substringToIndex:1] uppercaseString];
NSString *restOfComponent=[component substringFromIndex:1];
NSString *newComponent=[NSString stringWithFormat:@"%@%@",firstLetterUpper,restOfComponent];
[llamaCaseString appendString:newComponent];
}
NSString *firstLetterLower=[[llamaCaseString substringToIndex:1] lowercaseString];
[llamaCaseString replaceCharactersInRange:NSMakeRange(0, 1) withString:firstLetterLower];
return llamaCaseString;
}
@end

##TODO

  • methods to take in and return different types of key-value stores eg NSObjects and JSON strings.
  • better docs? any advice or questions are appriciated.
  • include some sensible string/ value transformations. (you should out this TransformerKit by @mattt)

##License

MIT

##Contribution

Is welcome!!

  • start a conversation in issues / make a pull request if you have code
  • KVMapper is tested with Kiwi so adding tests to your code would be nice, (also feel free to use OCUnit if you prefer)

Eternal thanks

About

A Library to map NSDictionary to an NSObject that can transform BOTH keys and values with a single map.

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

#KVMapper

A Library to map one NSDictionary to another that can transform BOTH keys and values with a single map. It also allows you to decouple any formatting you may have to do from one representation of data to another (eg. rest api -> internal model representation).

#####sidenote In trying to allow for as much customisation as possible, this tiny library makes very little assumptions which means you must be somewhat verbose. But see convenience methods that should greatly reduce code for the simpler cases.

##Installation

###cocoapods

add to your podfile:

Pod 'KVMapper' '~>0.3'

###manual

(pro-tip use cocoapods)

Drag contents of KVMapper folder to Xcode your favourite IDE.

##Usage

###instant attention grabbing example

You have a dictionary from a JSON Rest API that gives you a key-value store of cat names and their ages: the NSDictionary representaion looks like this:

NSDictionary *catsFromAPI=@{
@"maggy_the_ninja": @5,
@"magrat_the_rockstar": @6,
@"mable_the_wizard": @2,
… 15 or so more cats
@"milly_the_scratcher":@"3"
};

you want this in an NSDictionary which looks like this:

//llamaCaseCatNames
//ages are now in human years (sort of)
NSDictionary *catsForMyModel=@{
@"maggyTheNinja": @30,
@"magratTheRockstar": @36,
@"mableTheWizard": @12,
… 15 or so more cats
@"millyTheScratcher":@"3"
};

here's how you can do that:

 #import "KVMapper.h"
#import "Transformers.h" //see apendix, used to do snake_case to llameCase transformation
NSDictionary *catsMappingDict=[KVMapper mappingDictionaryForKeys:catsFromAPI.allKeys
defaultKeyTransformer:^NSString *(NSString *catName) {
return [Transformers snakeToLlamaCase:catName];
}
defaultValueTransformer:^id(NSNumber *catAge) {
float humanAge=catAge.floatValue+5;
return [NSNumber numberWithFloat:humanAge];
}];
NSDictionary *catsForMyModel=[KVMapper mappedDictionaryWithInputDictionary:catsFromAPI mappingDictionary:catsMappingDict];
//catsForMyModel is now as in the form @{@"maggyTheNinja": @30, …… etc.}

###Creating a dictionary

To create a dictionary from a dictionary, you need mappedDictionaryWithInputDictionary:mappingDictionary:

verbosely:

+(NSDictionary *)mappedDictionaryWithInputDictionary:(NSDictionary *)inputDict mappingDictionary:(NSDictionary *)mappingDict;
NSDictionary *myMappedDictionary=[KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];

This takes your input dictionary and a mapping dictionary.

####mapping dictionary

A mapping dictionary is an NSDictionary whose keys correspond to the keys of the input dictionary and the values are instances of KVMap.

#####KVMap

the KVMap object is just an NSObject with two properties:

  1. keyTransformationBlock
  2. valueTransformationBlock

The former receives and returns an NSString (the key) the latter receives and return id ie any object you want (the value).

######creating a KVMap

making a KVMap is a simple as setting the key and value transformation blocks:

KVMap *myKVMap=[[KVMap alloc] init];
myKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputString lowercaseString];
};
myKVMap.valueTransformationBlock=^(id *input){
return @5;
};

there is an initaliser initWithKeyTransformationBlock:valueTransformationBlock: to create one in a single step.

####creating a mapping dictionary

To create a mapping dictionary you must create the various KVMaps you need to map the input dictionary's key-value pairs into the form you want. Then set these as values to the key correspondong to the input dictionary's key:

// the input dictionary
NSDictionary *inputDict=@{
@"KEYONE":@1,
@"KEYTWO":@2,
@"myKEYTHREE":@"3",
};
//set up maps (you can use the same map if it applies to multiple KV pairs)
KVMap *myKeyOneAndTwoKVMap=[[KVMap alloc] init];
myKeyOneAndTwoKVMap.keyTransformationBlock=^(NSString *inputString){
return [NSSString stringWithFormat:@"my%@",[inputSting lowerCaseString]];
};
myKeyOneAndTwoKVMap.valueTransformationBlock=^(id *input){
return [NSString stringWithFormat:@"%i",input];
};
KVMap *myKeyThreeKVMap=[[KVMap alloc] init];
myKeyThreeKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputSting lowerCaseString];
};
//create mapping dict
NSDictionary *mappingDict=@{
@"KEYONE":myKeyOneAndTwoKVMap,
@"KEYTWO":myKeyOneAndTwoKVMap,
@"myKEYTHREE":myKeyThreeKVMap
};
// [KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];
// will return:
// @{
// @"mykeyone":@"1",
// @"mykeytwo":@"2",
// @"mykeythree":@"3"
// };

###Convenience Methods

Writing out all the mapping logic can quickly become tedious, especially when we often have to do the same transformation for a key, value or both.

so KVMapper has: KVMapsForKeys:defaultKeyTransformer: and KVMapsForKeys:defaultKeyTransformer:defaultValueTransformer:

so suppose we want to create a mappingDictionary for where all the keys are transformed to llamaCase and all the values (which we get as strings) are NSURLs.

 NSDictionary *mappingDict=[KVMapper mappingDictionaryForKeys:testDict.allKeys
defaultKeyTransformer:^(NSString *inKey){
return [Transformers snakeToLlamaCase:inKey];
}
defaultValueTransformer:^(id inValue){
NSURL *url=[NSURL URLWithString:inValue];
return url;
}];

This mappingDict can now be used in

##Appendix

###The random transformer class

//Transformers.h
#import <Foundation/Foundation.h>
@interface Transformers : NSObject
+(NSString *)snakeToLlamaCase:(NSString *)inKey;
@end
//Transformers.m
#import "Transformers.h"
@implementation Transformers
#pragma mark -string transformers
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"couldn't set value: %@ for undefkey:%@",[value description],key);
}
+(NSString *)snakeToLlamaCase:(NSString *)inKey{
NSArray *keyComponents=[inKey componentsSeparatedByString:@"_"];
return [self llamaCasFromComponents:keyComponents];
}
+(NSString *)llamaCasFromComponents:(NSArray *)components{
NSMutableString *llamaCaseString=[NSMutableString string];
for (NSString *component in components) {
NSString *firstLetterUpper=[[component substringToIndex:1] uppercaseString];
NSString *restOfComponent=[component substringFromIndex:1];
NSString *newComponent=[NSString stringWithFormat:@"%@%@",firstLetterUpper,restOfComponent];
[llamaCaseString appendString:newComponent];
}
NSString *firstLetterLower=[[llamaCaseString substringToIndex:1] lowercaseString];
[llamaCaseString replaceCharactersInRange:NSMakeRange(0, 1) withString:firstLetterLower];
return llamaCaseString;
}
@end

##TODO

  • methods to take in and return different types of key-value stores eg NSObjects and JSON strings.
  • better docs? any advice or questions are appriciated.
  • include some sensible string/ value transformations. (you should out this TransformerKit by @mattt)

##License

MIT

##Contribution

Is welcome!!

  • start a conversation in issues / make a pull request if you have code
  • KVMapper is tested with Kiwi so adding tests to your code would be nice, (also feel free to use OCUnit if you prefer)

Eternal thanks

About

A Library to map NSDictionary to an NSObject that can transform BOTH keys and values with a single map.

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

#KVMapper

A Library to map one NSDictionary to another that can transform BOTH keys and values with a single map. It also allows you to decouple any formatting you may have to do from one representation of data to another (eg. rest api -> internal model representation).

#####sidenote In trying to allow for as much customisation as possible, this tiny library makes very little assumptions which means you must be somewhat verbose. But see convenience methods that should greatly reduce code for the simpler cases.

##Installation

###cocoapods

add to your podfile:

Pod 'KVMapper' '~>0.3'

###manual

(pro-tip use cocoapods)

Drag contents of KVMapper folder to Xcode your favourite IDE.

##Usage

###instant attention grabbing example

You have a dictionary from a JSON Rest API that gives you a key-value store of cat names and their ages: the NSDictionary representaion looks like this:

NSDictionary *catsFromAPI=@{
@"maggy_the_ninja": @5,
@"magrat_the_rockstar": @6,
@"mable_the_wizard": @2,
… 15 or so more cats
@"milly_the_scratcher":@"3"
};

you want this in an NSDictionary which looks like this:

//llamaCaseCatNames
//ages are now in human years (sort of)
NSDictionary *catsForMyModel=@{
@"maggyTheNinja": @30,
@"magratTheRockstar": @36,
@"mableTheWizard": @12,
… 15 or so more cats
@"millyTheScratcher":@"3"
};

here's how you can do that:

 #import "KVMapper.h"
#import "Transformers.h" //see apendix, used to do snake_case to llameCase transformation
NSDictionary *catsMappingDict=[KVMapper mappingDictionaryForKeys:catsFromAPI.allKeys
defaultKeyTransformer:^NSString *(NSString *catName) {
return [Transformers snakeToLlamaCase:catName];
}
defaultValueTransformer:^id(NSNumber *catAge) {
float humanAge=catAge.floatValue+5;
return [NSNumber numberWithFloat:humanAge];
}];
NSDictionary *catsForMyModel=[KVMapper mappedDictionaryWithInputDictionary:catsFromAPI mappingDictionary:catsMappingDict];
//catsForMyModel is now as in the form @{@"maggyTheNinja": @30, …… etc.}

###Creating a dictionary

To create a dictionary from a dictionary, you need mappedDictionaryWithInputDictionary:mappingDictionary:

verbosely:

+(NSDictionary *)mappedDictionaryWithInputDictionary:(NSDictionary *)inputDict mappingDictionary:(NSDictionary *)mappingDict;
NSDictionary *myMappedDictionary=[KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];

This takes your input dictionary and a mapping dictionary.

####mapping dictionary

A mapping dictionary is an NSDictionary whose keys correspond to the keys of the input dictionary and the values are instances of KVMap.

#####KVMap

the KVMap object is just an NSObject with two properties:

  1. keyTransformationBlock
  2. valueTransformationBlock

The former receives and returns an NSString (the key) the latter receives and return id ie any object you want (the value).

######creating a KVMap

making a KVMap is a simple as setting the key and value transformation blocks:

KVMap *myKVMap=[[KVMap alloc] init];
myKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputString lowercaseString];
};
myKVMap.valueTransformationBlock=^(id *input){
return @5;
};

there is an initaliser initWithKeyTransformationBlock:valueTransformationBlock: to create one in a single step.

####creating a mapping dictionary

To create a mapping dictionary you must create the various KVMaps you need to map the input dictionary's key-value pairs into the form you want. Then set these as values to the key correspondong to the input dictionary's key:

// the input dictionary
NSDictionary *inputDict=@{
@"KEYONE":@1,
@"KEYTWO":@2,
@"myKEYTHREE":@"3",
};
//set up maps (you can use the same map if it applies to multiple KV pairs)
KVMap *myKeyOneAndTwoKVMap=[[KVMap alloc] init];
myKeyOneAndTwoKVMap.keyTransformationBlock=^(NSString *inputString){
return [NSSString stringWithFormat:@"my%@",[inputSting lowerCaseString]];
};
myKeyOneAndTwoKVMap.valueTransformationBlock=^(id *input){
return [NSString stringWithFormat:@"%i",input];
};
KVMap *myKeyThreeKVMap=[[KVMap alloc] init];
myKeyThreeKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputSting lowerCaseString];
};
//create mapping dict
NSDictionary *mappingDict=@{
@"KEYONE":myKeyOneAndTwoKVMap,
@"KEYTWO":myKeyOneAndTwoKVMap,
@"myKEYTHREE":myKeyThreeKVMap
};
// [KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];
// will return:
// @{
// @"mykeyone":@"1",
// @"mykeytwo":@"2",
// @"mykeythree":@"3"
// };

###Convenience Methods

Writing out all the mapping logic can quickly become tedious, especially when we often have to do the same transformation for a key, value or both.

so KVMapper has: KVMapsForKeys:defaultKeyTransformer: and KVMapsForKeys:defaultKeyTransformer:defaultValueTransformer:

so suppose we want to create a mappingDictionary for where all the keys are transformed to llamaCase and all the values (which we get as strings) are NSURLs.

 NSDictionary *mappingDict=[KVMapper mappingDictionaryForKeys:testDict.allKeys
defaultKeyTransformer:^(NSString *inKey){
return [Transformers snakeToLlamaCase:inKey];
}
defaultValueTransformer:^(id inValue){
NSURL *url=[NSURL URLWithString:inValue];
return url;
}];

This mappingDict can now be used in

##Appendix

###The random transformer class

//Transformers.h
#import <Foundation/Foundation.h>
@interface Transformers : NSObject
+(NSString *)snakeToLlamaCase:(NSString *)inKey;
@end
//Transformers.m
#import "Transformers.h"
@implementation Transformers
#pragma mark -string transformers
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"couldn't set value: %@ for undefkey:%@",[value description],key);
}
+(NSString *)snakeToLlamaCase:(NSString *)inKey{
NSArray *keyComponents=[inKey componentsSeparatedByString:@"_"];
return [self llamaCasFromComponents:keyComponents];
}
+(NSString *)llamaCasFromComponents:(NSArray *)components{
NSMutableString *llamaCaseString=[NSMutableString string];
for (NSString *component in components) {
NSString *firstLetterUpper=[[component substringToIndex:1] uppercaseString];
NSString *restOfComponent=[component substringFromIndex:1];
NSString *newComponent=[NSString stringWithFormat:@"%@%@",firstLetterUpper,restOfComponent];
[llamaCaseString appendString:newComponent];
}
NSString *firstLetterLower=[[llamaCaseString substringToIndex:1] lowercaseString];
[llamaCaseString replaceCharactersInRange:NSMakeRange(0, 1) withString:firstLetterLower];
return llamaCaseString;
}
@end

##TODO

  • methods to take in and return different types of key-value stores eg NSObjects and JSON strings.
  • better docs? any advice or questions are appriciated.
  • include some sensible string/ value transformations. (you should out this TransformerKit by @mattt)

##License

MIT

##Contribution

Is welcome!!

  • start a conversation in issues / make a pull request if you have code
  • KVMapper is tested with Kiwi so adding tests to your code would be nice, (also feel free to use OCUnit if you prefer)

Eternal thanks

About

A Library to map NSDictionary to an NSObject that can transform BOTH keys and values with a single map.

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

#KVMapper

A Library to map one NSDictionary to another that can transform BOTH keys and values with a single map. It also allows you to decouple any formatting you may have to do from one representation of data to another (eg. rest api -> internal model representation).

#####sidenote In trying to allow for as much customisation as possible, this tiny library makes very little assumptions which means you must be somewhat verbose. But see convenience methods that should greatly reduce code for the simpler cases.

##Installation

###cocoapods

add to your podfile:

Pod 'KVMapper' '~>0.3'

###manual

(pro-tip use cocoapods)

Drag contents of KVMapper folder to Xcode your favourite IDE.

##Usage

###instant attention grabbing example

You have a dictionary from a JSON Rest API that gives you a key-value store of cat names and their ages: the NSDictionary representaion looks like this:

NSDictionary *catsFromAPI=@{
@"maggy_the_ninja": @5,
@"magrat_the_rockstar": @6,
@"mable_the_wizard": @2,
… 15 or so more cats
@"milly_the_scratcher":@"3"
};

you want this in an NSDictionary which looks like this:

//llamaCaseCatNames
//ages are now in human years (sort of)
NSDictionary *catsForMyModel=@{
@"maggyTheNinja": @30,
@"magratTheRockstar": @36,
@"mableTheWizard": @12,
… 15 or so more cats
@"millyTheScratcher":@"3"
};

here's how you can do that:

 #import "KVMapper.h"
#import "Transformers.h" //see apendix, used to do snake_case to llameCase transformation
NSDictionary *catsMappingDict=[KVMapper mappingDictionaryForKeys:catsFromAPI.allKeys
defaultKeyTransformer:^NSString *(NSString *catName) {
return [Transformers snakeToLlamaCase:catName];
}
defaultValueTransformer:^id(NSNumber *catAge) {
float humanAge=catAge.floatValue+5;
return [NSNumber numberWithFloat:humanAge];
}];
NSDictionary *catsForMyModel=[KVMapper mappedDictionaryWithInputDictionary:catsFromAPI mappingDictionary:catsMappingDict];
//catsForMyModel is now as in the form @{@"maggyTheNinja": @30, …… etc.}

###Creating a dictionary

To create a dictionary from a dictionary, you need mappedDictionaryWithInputDictionary:mappingDictionary:

verbosely:

+(NSDictionary *)mappedDictionaryWithInputDictionary:(NSDictionary *)inputDict mappingDictionary:(NSDictionary *)mappingDict;
NSDictionary *myMappedDictionary=[KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];

This takes your input dictionary and a mapping dictionary.

####mapping dictionary

A mapping dictionary is an NSDictionary whose keys correspond to the keys of the input dictionary and the values are instances of KVMap.

#####KVMap

the KVMap object is just an NSObject with two properties:

  1. keyTransformationBlock
  2. valueTransformationBlock

The former receives and returns an NSString (the key) the latter receives and return id ie any object you want (the value).

######creating a KVMap

making a KVMap is a simple as setting the key and value transformation blocks:

KVMap *myKVMap=[[KVMap alloc] init];
myKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputString lowercaseString];
};
myKVMap.valueTransformationBlock=^(id *input){
return @5;
};

there is an initaliser initWithKeyTransformationBlock:valueTransformationBlock: to create one in a single step.

####creating a mapping dictionary

To create a mapping dictionary you must create the various KVMaps you need to map the input dictionary's key-value pairs into the form you want. Then set these as values to the key correspondong to the input dictionary's key:

// the input dictionary
NSDictionary *inputDict=@{
@"KEYONE":@1,
@"KEYTWO":@2,
@"myKEYTHREE":@"3",
};
//set up maps (you can use the same map if it applies to multiple KV pairs)
KVMap *myKeyOneAndTwoKVMap=[[KVMap alloc] init];
myKeyOneAndTwoKVMap.keyTransformationBlock=^(NSString *inputString){
return [NSSString stringWithFormat:@"my%@",[inputSting lowerCaseString]];
};
myKeyOneAndTwoKVMap.valueTransformationBlock=^(id *input){
return [NSString stringWithFormat:@"%i",input];
};
KVMap *myKeyThreeKVMap=[[KVMap alloc] init];
myKeyThreeKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputSting lowerCaseString];
};
//create mapping dict
NSDictionary *mappingDict=@{
@"KEYONE":myKeyOneAndTwoKVMap,
@"KEYTWO":myKeyOneAndTwoKVMap,
@"myKEYTHREE":myKeyThreeKVMap
};
// [KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];
// will return:
// @{
// @"mykeyone":@"1",
// @"mykeytwo":@"2",
// @"mykeythree":@"3"
// };

###Convenience Methods

Writing out all the mapping logic can quickly become tedious, especially when we often have to do the same transformation for a key, value or both.

so KVMapper has: KVMapsForKeys:defaultKeyTransformer: and KVMapsForKeys:defaultKeyTransformer:defaultValueTransformer:

so suppose we want to create a mappingDictionary for where all the keys are transformed to llamaCase and all the values (which we get as strings) are NSURLs.

 NSDictionary *mappingDict=[KVMapper mappingDictionaryForKeys:testDict.allKeys
defaultKeyTransformer:^(NSString *inKey){
return [Transformers snakeToLlamaCase:inKey];
}
defaultValueTransformer:^(id inValue){
NSURL *url=[NSURL URLWithString:inValue];
return url;
}];

This mappingDict can now be used in

##Appendix

###The random transformer class

//Transformers.h
#import <Foundation/Foundation.h>
@interface Transformers : NSObject
+(NSString *)snakeToLlamaCase:(NSString *)inKey;
@end
//Transformers.m
#import "Transformers.h"
@implementation Transformers
#pragma mark -string transformers
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"couldn't set value: %@ for undefkey:%@",[value description],key);
}
+(NSString *)snakeToLlamaCase:(NSString *)inKey{
NSArray *keyComponents=[inKey componentsSeparatedByString:@"_"];
return [self llamaCasFromComponents:keyComponents];
}
+(NSString *)llamaCasFromComponents:(NSArray *)components{
NSMutableString *llamaCaseString=[NSMutableString string];
for (NSString *component in components) {
NSString *firstLetterUpper=[[component substringToIndex:1] uppercaseString];
NSString *restOfComponent=[component substringFromIndex:1];
NSString *newComponent=[NSString stringWithFormat:@"%@%@",firstLetterUpper,restOfComponent];
[llamaCaseString appendString:newComponent];
}
NSString *firstLetterLower=[[llamaCaseString substringToIndex:1] lowercaseString];
[llamaCaseString replaceCharactersInRange:NSMakeRange(0, 1) withString:firstLetterLower];
return llamaCaseString;
}
@end

##TODO

  • methods to take in and return different types of key-value stores eg NSObjects and JSON strings.
  • better docs? any advice or questions are appriciated.
  • include some sensible string/ value transformations. (you should out this TransformerKit by @mattt)

##License

MIT

##Contribution

Is welcome!!

  • start a conversation in issues / make a pull request if you have code
  • KVMapper is tested with Kiwi so adding tests to your code would be nice, (also feel free to use OCUnit if you prefer)

Eternal thanks

About

A Library to map NSDictionary to an NSObject that can transform BOTH keys and values with a single map.

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

#KVMapper

A Library to map one NSDictionary to another that can transform BOTH keys and values with a single map. It also allows you to decouple any formatting you may have to do from one representation of data to another (eg. rest api -> internal model representation).

#####sidenote In trying to allow for as much customisation as possible, this tiny library makes very little assumptions which means you must be somewhat verbose. But see convenience methods that should greatly reduce code for the simpler cases.

##Installation

###cocoapods

add to your podfile:

Pod 'KVMapper' '~>0.3'

###manual

(pro-tip use cocoapods)

Drag contents of KVMapper folder to Xcode your favourite IDE.

##Usage

###instant attention grabbing example

You have a dictionary from a JSON Rest API that gives you a key-value store of cat names and their ages: the NSDictionary representaion looks like this:

NSDictionary *catsFromAPI=@{
@"maggy_the_ninja": @5,
@"magrat_the_rockstar": @6,
@"mable_the_wizard": @2,
… 15 or so more cats
@"milly_the_scratcher":@"3"
};

you want this in an NSDictionary which looks like this:

//llamaCaseCatNames
//ages are now in human years (sort of)
NSDictionary *catsForMyModel=@{
@"maggyTheNinja": @30,
@"magratTheRockstar": @36,
@"mableTheWizard": @12,
… 15 or so more cats
@"millyTheScratcher":@"3"
};

here's how you can do that:

 #import "KVMapper.h"
#import "Transformers.h" //see apendix, used to do snake_case to llameCase transformation
NSDictionary *catsMappingDict=[KVMapper mappingDictionaryForKeys:catsFromAPI.allKeys
defaultKeyTransformer:^NSString *(NSString *catName) {
return [Transformers snakeToLlamaCase:catName];
}
defaultValueTransformer:^id(NSNumber *catAge) {
float humanAge=catAge.floatValue+5;
return [NSNumber numberWithFloat:humanAge];
}];
NSDictionary *catsForMyModel=[KVMapper mappedDictionaryWithInputDictionary:catsFromAPI mappingDictionary:catsMappingDict];
//catsForMyModel is now as in the form @{@"maggyTheNinja": @30, …… etc.}

###Creating a dictionary

To create a dictionary from a dictionary, you need mappedDictionaryWithInputDictionary:mappingDictionary:

verbosely:

+(NSDictionary *)mappedDictionaryWithInputDictionary:(NSDictionary *)inputDict mappingDictionary:(NSDictionary *)mappingDict;
NSDictionary *myMappedDictionary=[KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];

This takes your input dictionary and a mapping dictionary.

####mapping dictionary

A mapping dictionary is an NSDictionary whose keys correspond to the keys of the input dictionary and the values are instances of KVMap.

#####KVMap

the KVMap object is just an NSObject with two properties:

  1. keyTransformationBlock
  2. valueTransformationBlock

The former receives and returns an NSString (the key) the latter receives and return id ie any object you want (the value).

######creating a KVMap

making a KVMap is a simple as setting the key and value transformation blocks:

KVMap *myKVMap=[[KVMap alloc] init];
myKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputString lowercaseString];
};
myKVMap.valueTransformationBlock=^(id *input){
return @5;
};

there is an initaliser initWithKeyTransformationBlock:valueTransformationBlock: to create one in a single step.

####creating a mapping dictionary

To create a mapping dictionary you must create the various KVMaps you need to map the input dictionary's key-value pairs into the form you want. Then set these as values to the key correspondong to the input dictionary's key:

// the input dictionary
NSDictionary *inputDict=@{
@"KEYONE":@1,
@"KEYTWO":@2,
@"myKEYTHREE":@"3",
};
//set up maps (you can use the same map if it applies to multiple KV pairs)
KVMap *myKeyOneAndTwoKVMap=[[KVMap alloc] init];
myKeyOneAndTwoKVMap.keyTransformationBlock=^(NSString *inputString){
return [NSSString stringWithFormat:@"my%@",[inputSting lowerCaseString]];
};
myKeyOneAndTwoKVMap.valueTransformationBlock=^(id *input){
return [NSString stringWithFormat:@"%i",input];
};
KVMap *myKeyThreeKVMap=[[KVMap alloc] init];
myKeyThreeKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputSting lowerCaseString];
};
//create mapping dict
NSDictionary *mappingDict=@{
@"KEYONE":myKeyOneAndTwoKVMap,
@"KEYTWO":myKeyOneAndTwoKVMap,
@"myKEYTHREE":myKeyThreeKVMap
};
// [KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];
// will return:
// @{
// @"mykeyone":@"1",
// @"mykeytwo":@"2",
// @"mykeythree":@"3"
// };

###Convenience Methods

Writing out all the mapping logic can quickly become tedious, especially when we often have to do the same transformation for a key, value or both.

so KVMapper has: KVMapsForKeys:defaultKeyTransformer: and KVMapsForKeys:defaultKeyTransformer:defaultValueTransformer:

so suppose we want to create a mappingDictionary for where all the keys are transformed to llamaCase and all the values (which we get as strings) are NSURLs.

 NSDictionary *mappingDict=[KVMapper mappingDictionaryForKeys:testDict.allKeys
defaultKeyTransformer:^(NSString *inKey){
return [Transformers snakeToLlamaCase:inKey];
}
defaultValueTransformer:^(id inValue){
NSURL *url=[NSURL URLWithString:inValue];
return url;
}];

This mappingDict can now be used in

##Appendix

###The random transformer class

//Transformers.h
#import <Foundation/Foundation.h>
@interface Transformers : NSObject
+(NSString *)snakeToLlamaCase:(NSString *)inKey;
@end
//Transformers.m
#import "Transformers.h"
@implementation Transformers
#pragma mark -string transformers
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"couldn't set value: %@ for undefkey:%@",[value description],key);
}
+(NSString *)snakeToLlamaCase:(NSString *)inKey{
NSArray *keyComponents=[inKey componentsSeparatedByString:@"_"];
return [self llamaCasFromComponents:keyComponents];
}
+(NSString *)llamaCasFromComponents:(NSArray *)components{
NSMutableString *llamaCaseString=[NSMutableString string];
for (NSString *component in components) {
NSString *firstLetterUpper=[[component substringToIndex:1] uppercaseString];
NSString *restOfComponent=[component substringFromIndex:1];
NSString *newComponent=[NSString stringWithFormat:@"%@%@",firstLetterUpper,restOfComponent];
[llamaCaseString appendString:newComponent];
}
NSString *firstLetterLower=[[llamaCaseString substringToIndex:1] lowercaseString];
[llamaCaseString replaceCharactersInRange:NSMakeRange(0, 1) withString:firstLetterLower];
return llamaCaseString;
}
@end

##TODO

  • methods to take in and return different types of key-value stores eg NSObjects and JSON strings.
  • better docs? any advice or questions are appriciated.
  • include some sensible string/ value transformations. (you should out this TransformerKit by @mattt)

##License

MIT

##Contribution

Is welcome!!

  • start a conversation in issues / make a pull request if you have code
  • KVMapper is tested with Kiwi so adding tests to your code would be nice, (also feel free to use OCUnit if you prefer)

Eternal thanks

About

A Library to map NSDictionary to an NSObject that can transform BOTH keys and values with a single map.

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

#KVMapper

A Library to map one NSDictionary to another that can transform BOTH keys and values with a single map. It also allows you to decouple any formatting you may have to do from one representation of data to another (eg. rest api -> internal model representation).

#####sidenote In trying to allow for as much customisation as possible, this tiny library makes very little assumptions which means you must be somewhat verbose. But see convenience methods that should greatly reduce code for the simpler cases.

##Installation

###cocoapods

add to your podfile:

Pod 'KVMapper' '~>0.3'

###manual

(pro-tip use cocoapods)

Drag contents of KVMapper folder to Xcode your favourite IDE.

##Usage

###instant attention grabbing example

You have a dictionary from a JSON Rest API that gives you a key-value store of cat names and their ages: the NSDictionary representaion looks like this:

NSDictionary *catsFromAPI=@{
@"maggy_the_ninja": @5,
@"magrat_the_rockstar": @6,
@"mable_the_wizard": @2,
… 15 or so more cats
@"milly_the_scratcher":@"3"
};

you want this in an NSDictionary which looks like this:

//llamaCaseCatNames
//ages are now in human years (sort of)
NSDictionary *catsForMyModel=@{
@"maggyTheNinja": @30,
@"magratTheRockstar": @36,
@"mableTheWizard": @12,
… 15 or so more cats
@"millyTheScratcher":@"3"
};

here's how you can do that:

 #import "KVMapper.h"
#import "Transformers.h" //see apendix, used to do snake_case to llameCase transformation
NSDictionary *catsMappingDict=[KVMapper mappingDictionaryForKeys:catsFromAPI.allKeys
defaultKeyTransformer:^NSString *(NSString *catName) {
return [Transformers snakeToLlamaCase:catName];
}
defaultValueTransformer:^id(NSNumber *catAge) {
float humanAge=catAge.floatValue+5;
return [NSNumber numberWithFloat:humanAge];
}];
NSDictionary *catsForMyModel=[KVMapper mappedDictionaryWithInputDictionary:catsFromAPI mappingDictionary:catsMappingDict];
//catsForMyModel is now as in the form @{@"maggyTheNinja": @30, …… etc.}

###Creating a dictionary

To create a dictionary from a dictionary, you need mappedDictionaryWithInputDictionary:mappingDictionary:

verbosely:

+(NSDictionary *)mappedDictionaryWithInputDictionary:(NSDictionary *)inputDict mappingDictionary:(NSDictionary *)mappingDict;
NSDictionary *myMappedDictionary=[KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];

This takes your input dictionary and a mapping dictionary.

####mapping dictionary

A mapping dictionary is an NSDictionary whose keys correspond to the keys of the input dictionary and the values are instances of KVMap.

#####KVMap

the KVMap object is just an NSObject with two properties:

  1. keyTransformationBlock
  2. valueTransformationBlock

The former receives and returns an NSString (the key) the latter receives and return id ie any object you want (the value).

######creating a KVMap

making a KVMap is a simple as setting the key and value transformation blocks:

KVMap *myKVMap=[[KVMap alloc] init];
myKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputString lowercaseString];
};
myKVMap.valueTransformationBlock=^(id *input){
return @5;
};

there is an initaliser initWithKeyTransformationBlock:valueTransformationBlock: to create one in a single step.

####creating a mapping dictionary

To create a mapping dictionary you must create the various KVMaps you need to map the input dictionary's key-value pairs into the form you want. Then set these as values to the key correspondong to the input dictionary's key:

// the input dictionary
NSDictionary *inputDict=@{
@"KEYONE":@1,
@"KEYTWO":@2,
@"myKEYTHREE":@"3",
};
//set up maps (you can use the same map if it applies to multiple KV pairs)
KVMap *myKeyOneAndTwoKVMap=[[KVMap alloc] init];
myKeyOneAndTwoKVMap.keyTransformationBlock=^(NSString *inputString){
return [NSSString stringWithFormat:@"my%@",[inputSting lowerCaseString]];
};
myKeyOneAndTwoKVMap.valueTransformationBlock=^(id *input){
return [NSString stringWithFormat:@"%i",input];
};
KVMap *myKeyThreeKVMap=[[KVMap alloc] init];
myKeyThreeKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputSting lowerCaseString];
};
//create mapping dict
NSDictionary *mappingDict=@{
@"KEYONE":myKeyOneAndTwoKVMap,
@"KEYTWO":myKeyOneAndTwoKVMap,
@"myKEYTHREE":myKeyThreeKVMap
};
// [KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];
// will return:
// @{
// @"mykeyone":@"1",
// @"mykeytwo":@"2",
// @"mykeythree":@"3"
// };

###Convenience Methods

Writing out all the mapping logic can quickly become tedious, especially when we often have to do the same transformation for a key, value or both.

so KVMapper has: KVMapsForKeys:defaultKeyTransformer: and KVMapsForKeys:defaultKeyTransformer:defaultValueTransformer:

so suppose we want to create a mappingDictionary for where all the keys are transformed to llamaCase and all the values (which we get as strings) are NSURLs.

 NSDictionary *mappingDict=[KVMapper mappingDictionaryForKeys:testDict.allKeys
defaultKeyTransformer:^(NSString *inKey){
return [Transformers snakeToLlamaCase:inKey];
}
defaultValueTransformer:^(id inValue){
NSURL *url=[NSURL URLWithString:inValue];
return url;
}];

This mappingDict can now be used in

##Appendix

###The random transformer class

//Transformers.h
#import <Foundation/Foundation.h>
@interface Transformers : NSObject
+(NSString *)snakeToLlamaCase:(NSString *)inKey;
@end
//Transformers.m
#import "Transformers.h"
@implementation Transformers
#pragma mark -string transformers
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"couldn't set value: %@ for undefkey:%@",[value description],key);
}
+(NSString *)snakeToLlamaCase:(NSString *)inKey{
NSArray *keyComponents=[inKey componentsSeparatedByString:@"_"];
return [self llamaCasFromComponents:keyComponents];
}
+(NSString *)llamaCasFromComponents:(NSArray *)components{
NSMutableString *llamaCaseString=[NSMutableString string];
for (NSString *component in components) {
NSString *firstLetterUpper=[[component substringToIndex:1] uppercaseString];
NSString *restOfComponent=[component substringFromIndex:1];
NSString *newComponent=[NSString stringWithFormat:@"%@%@",firstLetterUpper,restOfComponent];
[llamaCaseString appendString:newComponent];
}
NSString *firstLetterLower=[[llamaCaseString substringToIndex:1] lowercaseString];
[llamaCaseString replaceCharactersInRange:NSMakeRange(0, 1) withString:firstLetterLower];
return llamaCaseString;
}
@end

##TODO

  • methods to take in and return different types of key-value stores eg NSObjects and JSON strings.
  • better docs? any advice or questions are appriciated.
  • include some sensible string/ value transformations. (you should out this TransformerKit by @mattt)

##License

MIT

##Contribution

Is welcome!!

  • start a conversation in issues / make a pull request if you have code
  • KVMapper is tested with Kiwi so adding tests to your code would be nice, (also feel free to use OCUnit if you prefer)

Eternal thanks

About

A Library to map NSDictionary to an NSObject that can transform BOTH keys and values with a single map.

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

#KVMapper

A Library to map one NSDictionary to another that can transform BOTH keys and values with a single map. It also allows you to decouple any formatting you may have to do from one representation of data to another (eg. rest api -> internal model representation).

#####sidenote In trying to allow for as much customisation as possible, this tiny library makes very little assumptions which means you must be somewhat verbose. But see convenience methods that should greatly reduce code for the simpler cases.

##Installation

###cocoapods

add to your podfile:

Pod 'KVMapper' '~>0.3'

###manual

(pro-tip use cocoapods)

Drag contents of KVMapper folder to Xcode your favourite IDE.

##Usage

###instant attention grabbing example

You have a dictionary from a JSON Rest API that gives you a key-value store of cat names and their ages: the NSDictionary representaion looks like this:

NSDictionary *catsFromAPI=@{
@"maggy_the_ninja": @5,
@"magrat_the_rockstar": @6,
@"mable_the_wizard": @2,
… 15 or so more cats
@"milly_the_scratcher":@"3"
};

you want this in an NSDictionary which looks like this:

//llamaCaseCatNames
//ages are now in human years (sort of)
NSDictionary *catsForMyModel=@{
@"maggyTheNinja": @30,
@"magratTheRockstar": @36,
@"mableTheWizard": @12,
… 15 or so more cats
@"millyTheScratcher":@"3"
};

here's how you can do that:

 #import "KVMapper.h"
#import "Transformers.h" //see apendix, used to do snake_case to llameCase transformation
NSDictionary *catsMappingDict=[KVMapper mappingDictionaryForKeys:catsFromAPI.allKeys
defaultKeyTransformer:^NSString *(NSString *catName) {
return [Transformers snakeToLlamaCase:catName];
}
defaultValueTransformer:^id(NSNumber *catAge) {
float humanAge=catAge.floatValue+5;
return [NSNumber numberWithFloat:humanAge];
}];
NSDictionary *catsForMyModel=[KVMapper mappedDictionaryWithInputDictionary:catsFromAPI mappingDictionary:catsMappingDict];
//catsForMyModel is now as in the form @{@"maggyTheNinja": @30, …… etc.}

###Creating a dictionary

To create a dictionary from a dictionary, you need mappedDictionaryWithInputDictionary:mappingDictionary:

verbosely:

+(NSDictionary *)mappedDictionaryWithInputDictionary:(NSDictionary *)inputDict mappingDictionary:(NSDictionary *)mappingDict;
NSDictionary *myMappedDictionary=[KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];

This takes your input dictionary and a mapping dictionary.

####mapping dictionary

A mapping dictionary is an NSDictionary whose keys correspond to the keys of the input dictionary and the values are instances of KVMap.

#####KVMap

the KVMap object is just an NSObject with two properties:

  1. keyTransformationBlock
  2. valueTransformationBlock

The former receives and returns an NSString (the key) the latter receives and return id ie any object you want (the value).

######creating a KVMap

making a KVMap is a simple as setting the key and value transformation blocks:

KVMap *myKVMap=[[KVMap alloc] init];
myKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputString lowercaseString];
};
myKVMap.valueTransformationBlock=^(id *input){
return @5;
};

there is an initaliser initWithKeyTransformationBlock:valueTransformationBlock: to create one in a single step.

####creating a mapping dictionary

To create a mapping dictionary you must create the various KVMaps you need to map the input dictionary's key-value pairs into the form you want. Then set these as values to the key correspondong to the input dictionary's key:

// the input dictionary
NSDictionary *inputDict=@{
@"KEYONE":@1,
@"KEYTWO":@2,
@"myKEYTHREE":@"3",
};
//set up maps (you can use the same map if it applies to multiple KV pairs)
KVMap *myKeyOneAndTwoKVMap=[[KVMap alloc] init];
myKeyOneAndTwoKVMap.keyTransformationBlock=^(NSString *inputString){
return [NSSString stringWithFormat:@"my%@",[inputSting lowerCaseString]];
};
myKeyOneAndTwoKVMap.valueTransformationBlock=^(id *input){
return [NSString stringWithFormat:@"%i",input];
};
KVMap *myKeyThreeKVMap=[[KVMap alloc] init];
myKeyThreeKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputSting lowerCaseString];
};
//create mapping dict
NSDictionary *mappingDict=@{
@"KEYONE":myKeyOneAndTwoKVMap,
@"KEYTWO":myKeyOneAndTwoKVMap,
@"myKEYTHREE":myKeyThreeKVMap
};
// [KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];
// will return:
// @{
// @"mykeyone":@"1",
// @"mykeytwo":@"2",
// @"mykeythree":@"3"
// };

###Convenience Methods

Writing out all the mapping logic can quickly become tedious, especially when we often have to do the same transformation for a key, value or both.

so KVMapper has: KVMapsForKeys:defaultKeyTransformer: and KVMapsForKeys:defaultKeyTransformer:defaultValueTransformer:

so suppose we want to create a mappingDictionary for where all the keys are transformed to llamaCase and all the values (which we get as strings) are NSURLs.

 NSDictionary *mappingDict=[KVMapper mappingDictionaryForKeys:testDict.allKeys
defaultKeyTransformer:^(NSString *inKey){
return [Transformers snakeToLlamaCase:inKey];
}
defaultValueTransformer:^(id inValue){
NSURL *url=[NSURL URLWithString:inValue];
return url;
}];

This mappingDict can now be used in

##Appendix

###The random transformer class

//Transformers.h
#import <Foundation/Foundation.h>
@interface Transformers : NSObject
+(NSString *)snakeToLlamaCase:(NSString *)inKey;
@end
//Transformers.m
#import "Transformers.h"
@implementation Transformers
#pragma mark -string transformers
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"couldn't set value: %@ for undefkey:%@",[value description],key);
}
+(NSString *)snakeToLlamaCase:(NSString *)inKey{
NSArray *keyComponents=[inKey componentsSeparatedByString:@"_"];
return [self llamaCasFromComponents:keyComponents];
}
+(NSString *)llamaCasFromComponents:(NSArray *)components{
NSMutableString *llamaCaseString=[NSMutableString string];
for (NSString *component in components) {
NSString *firstLetterUpper=[[component substringToIndex:1] uppercaseString];
NSString *restOfComponent=[component substringFromIndex:1];
NSString *newComponent=[NSString stringWithFormat:@"%@%@",firstLetterUpper,restOfComponent];
[llamaCaseString appendString:newComponent];
}
NSString *firstLetterLower=[[llamaCaseString substringToIndex:1] lowercaseString];
[llamaCaseString replaceCharactersInRange:NSMakeRange(0, 1) withString:firstLetterLower];
return llamaCaseString;
}
@end

##TODO

  • methods to take in and return different types of key-value stores eg NSObjects and JSON strings.
  • better docs? any advice or questions are appriciated.
  • include some sensible string/ value transformations. (you should out this TransformerKit by @mattt)

##License

MIT

##Contribution

Is welcome!!

  • start a conversation in issues / make a pull request if you have code
  • KVMapper is tested with Kiwi so adding tests to your code would be nice, (also feel free to use OCUnit if you prefer)

Eternal thanks

About

A Library to map NSDictionary to an NSObject that can transform BOTH keys and values with a single map.

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

#KVMapper

A Library to map one NSDictionary to another that can transform BOTH keys and values with a single map. It also allows you to decouple any formatting you may have to do from one representation of data to another (eg. rest api -> internal model representation).

#####sidenote In trying to allow for as much customisation as possible, this tiny library makes very little assumptions which means you must be somewhat verbose. But see convenience methods that should greatly reduce code for the simpler cases.

##Installation

###cocoapods

add to your podfile:

Pod 'KVMapper' '~>0.3'

###manual

(pro-tip use cocoapods)

Drag contents of KVMapper folder to Xcode your favourite IDE.

##Usage

###instant attention grabbing example

You have a dictionary from a JSON Rest API that gives you a key-value store of cat names and their ages: the NSDictionary representaion looks like this:

NSDictionary *catsFromAPI=@{
@"maggy_the_ninja": @5,
@"magrat_the_rockstar": @6,
@"mable_the_wizard": @2,
… 15 or so more cats
@"milly_the_scratcher":@"3"
};

you want this in an NSDictionary which looks like this:

//llamaCaseCatNames
//ages are now in human years (sort of)
NSDictionary *catsForMyModel=@{
@"maggyTheNinja": @30,
@"magratTheRockstar": @36,
@"mableTheWizard": @12,
… 15 or so more cats
@"millyTheScratcher":@"3"
};

here's how you can do that:

 #import "KVMapper.h"
#import "Transformers.h" //see apendix, used to do snake_case to llameCase transformation
NSDictionary *catsMappingDict=[KVMapper mappingDictionaryForKeys:catsFromAPI.allKeys
defaultKeyTransformer:^NSString *(NSString *catName) {
return [Transformers snakeToLlamaCase:catName];
}
defaultValueTransformer:^id(NSNumber *catAge) {
float humanAge=catAge.floatValue+5;
return [NSNumber numberWithFloat:humanAge];
}];
NSDictionary *catsForMyModel=[KVMapper mappedDictionaryWithInputDictionary:catsFromAPI mappingDictionary:catsMappingDict];
//catsForMyModel is now as in the form @{@"maggyTheNinja": @30, …… etc.}

###Creating a dictionary

To create a dictionary from a dictionary, you need mappedDictionaryWithInputDictionary:mappingDictionary:

verbosely:

+(NSDictionary *)mappedDictionaryWithInputDictionary:(NSDictionary *)inputDict mappingDictionary:(NSDictionary *)mappingDict;
NSDictionary *myMappedDictionary=[KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];

This takes your input dictionary and a mapping dictionary.

####mapping dictionary

A mapping dictionary is an NSDictionary whose keys correspond to the keys of the input dictionary and the values are instances of KVMap.

#####KVMap

the KVMap object is just an NSObject with two properties:

  1. keyTransformationBlock
  2. valueTransformationBlock

The former receives and returns an NSString (the key) the latter receives and return id ie any object you want (the value).

######creating a KVMap

making a KVMap is a simple as setting the key and value transformation blocks:

KVMap *myKVMap=[[KVMap alloc] init];
myKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputString lowercaseString];
};
myKVMap.valueTransformationBlock=^(id *input){
return @5;
};

there is an initaliser initWithKeyTransformationBlock:valueTransformationBlock: to create one in a single step.

####creating a mapping dictionary

To create a mapping dictionary you must create the various KVMaps you need to map the input dictionary's key-value pairs into the form you want. Then set these as values to the key correspondong to the input dictionary's key:

// the input dictionary
NSDictionary *inputDict=@{
@"KEYONE":@1,
@"KEYTWO":@2,
@"myKEYTHREE":@"3",
};
//set up maps (you can use the same map if it applies to multiple KV pairs)
KVMap *myKeyOneAndTwoKVMap=[[KVMap alloc] init];
myKeyOneAndTwoKVMap.keyTransformationBlock=^(NSString *inputString){
return [NSSString stringWithFormat:@"my%@",[inputSting lowerCaseString]];
};
myKeyOneAndTwoKVMap.valueTransformationBlock=^(id *input){
return [NSString stringWithFormat:@"%i",input];
};
KVMap *myKeyThreeKVMap=[[KVMap alloc] init];
myKeyThreeKVMap.keyTransformationBlock=^(NSString *inputString){
return [inputSting lowerCaseString];
};
//create mapping dict
NSDictionary *mappingDict=@{
@"KEYONE":myKeyOneAndTwoKVMap,
@"KEYTWO":myKeyOneAndTwoKVMap,
@"myKEYTHREE":myKeyThreeKVMap
};
// [KVMapper mappedDictionaryWithInputDictionary:inputDict mappingDictionary:mappingDict];
// will return:
// @{
// @"mykeyone":@"1",
// @"mykeytwo":@"2",
// @"mykeythree":@"3"
// };

###Convenience Methods

Writing out all the mapping logic can quickly become tedious, especially when we often have to do the same transformation for a key, value or both.

so KVMapper has: KVMapsForKeys:defaultKeyTransformer: and KVMapsForKeys:defaultKeyTransformer:defaultValueTransformer:

so suppose we want to create a mappingDictionary for where all the keys are transformed to llamaCase and all the values (which we get as strings) are NSURLs.

 NSDictionary *mappingDict=[KVMapper mappingDictionaryForKeys:testDict.allKeys
defaultKeyTransformer:^(NSString *inKey){
return [Transformers snakeToLlamaCase:inKey];
}
defaultValueTransformer:^(id inValue){
NSURL *url=[NSURL URLWithString:inValue];
return url;
}];

This mappingDict can now be used in

##Appendix

###The random transformer class

//Transformers.h
#import <Foundation/Foundation.h>
@interface Transformers : NSObject
+(NSString *)snakeToLlamaCase:(NSString *)inKey;
@end
//Transformers.m
#import "Transformers.h"
@implementation Transformers
#pragma mark -string transformers
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"couldn't set value: %@ for undefkey:%@",[value description],key);
}
+(NSString *)snakeToLlamaCase:(NSString *)inKey{
NSArray *keyComponents=[inKey componentsSeparatedByString:@"_"];
return [self llamaCasFromComponents:keyComponents];
}
+(NSString *)llamaCasFromComponents:(NSArray *)components{
NSMutableString *llamaCaseString=[NSMutableString string];
for (NSString *component in components) {
NSString *firstLetterUpper=[[component substringToIndex:1] uppercaseString];
NSString *restOfComponent=[component substringFromIndex:1];
NSString *newComponent=[NSString stringWithFormat:@"%@%@",firstLetterUpper,restOfComponent];
[llamaCaseString appendString:newComponent];
}
NSString *firstLetterLower=[[llamaCaseString substringToIndex:1] lowercaseString];
[llamaCaseString replaceCharactersInRange:NSMakeRange(0, 1) withString:firstLetterLower];
return llamaCaseString;
}
@end

##TODO

  • methods to take in and return different types of key-value stores eg NSObjects and JSON strings.
  • better docs? any advice or questions are appriciated.
  • include some sensible string/ value transformations. (you should out this TransformerKit by @mattt)

##License

MIT

##Contribution

Is welcome!!

  • start a conversation in issues / make a pull request if you have code
  • KVMapper is tested with Kiwi so adding tests to your code would be nice, (also feel free to use OCUnit if you prefer)

Eternal thanks

About

A Library to map NSDictionary to an NSObject that can transform BOTH keys and values with a single map.

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages