Created
April 18, 2011 13:42
-
-
Save valexa/925355 to your computer and use it in GitHub Desktop.
a method for changing objects inside deeply nested dictionaries
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { | |
//create a example nested dictionary with just 4 levels | |
NSMutableDictionary *parent = [[[NSMutableDictionary alloc] init] autorelease]; | |
NSDictionary *thirdChild = [NSDictionary dictionaryWithObjectsAndKeys:@"bi",@"l4",@"nar",@"l4_", nil]; | |
NSDictionary *secondChild = [NSDictionary dictionaryWithObjectsAndKeys:thirdChild,@"l3",@"bar",@"l3_", nil]; | |
NSDictionary *firstChild = [NSDictionary dictionaryWithObjectsAndKeys:secondChild,@"l2",secondChild,@"l2_", nil]; | |
[parent setObject:firstChild forKey:@"l1"]; | |
[parent setObject:firstChild forKey:@"l1_"]; | |
//NSLog(@"%@",[parent description]); | |
//NSLog(@"Was: %@",[[[parent objectForKey:@"l1_"] objectForKey:@"l2_"] objectForKey:@"l3_"]); | |
NSLog(@"Was: %@",[parent valueForKeyPath:@"l1_.l2_.l3_"]); | |
//let's change the value of l1_/l2_/l3_ to baz, the goal is a one line call to a method with minimal overhead | |
//manual method for reference, gets out of hand fast when dealing with deeply nested structures and has no type checking | |
if (1 == 2) { | |
NSMutableDictionary *firstChild = [[parent objectForKey:@"l1_"] mutableCopy]; | |
NSMutableDictionary *secondChild = [[firstChild objectForKey:@"l2_"] mutableCopy]; | |
[secondChild setObject:@"baz" forKey:@"l3_"]; | |
[firstChild setObject:secondChild forKey:@"l2_"]; | |
[secondChild release]; | |
[parent setObject:firstChild forKey:@"l1_"]; | |
[firstChild release]; | |
} | |
//proposed method | |
NSDictionary *changed_parent = [self editNestedDict:parent setObject:@"baz" forKeyHierarchy:[NSArray arrayWithObjects:@"l1_",@"l2_",@"l3_", nil]]; | |
//NSLog(@"%@",[changed_parent description]); | |
//NSLog(@"Is: %@",[[[changed_parent objectForKey:@"l1_"] objectForKey:@"l2_"] objectForKey:@"l3_"]); | |
NSLog(@"Is: %@",[changed_parent valueForKeyPath:@"l1_.l2_.l3_"]); | |
} | |
-(NSDictionary*)editNestedDict:(NSDictionary*)dict setObject:(id)object forKeyHierarchy:(NSArray*)hierarchy{ | |
if (dict == nil) return dict; | |
if (![dict isKindOfClass:[NSDictionary class]]) return dict; | |
NSMutableDictionary *parent = [[dict mutableCopy] autorelease]; | |
//drill down mutating each dict along the way | |
NSMutableArray *structure = [NSMutableArray arrayWithCapacity:1]; | |
NSMutableDictionary *prev = parent; | |
for (id key in hierarchy) { | |
if (key != [hierarchy lastObject]) { | |
prev = [[[prev objectForKey:key] mutableCopy] autorelease]; | |
if (![prev isKindOfClass:[NSDictionary class]]) return dict; | |
[structure addObject:prev]; | |
NSLog(@"loading %@",key); | |
}else{ | |
NSLog(@"changing %@",key); | |
} | |
} | |
//do the change | |
[[structure lastObject] setObject:object forKey:[hierarchy lastObject]]; | |
//drill back up saving the changes each step along the way | |
for (int c = [structure count]-1; c >= 0; c--) { | |
if (c == 0) { | |
[parent setObject:[structure objectAtIndex:c] forKey:[hierarchy objectAtIndex:c]]; | |
}else{ | |
[[structure objectAtIndex:c-1] setObject:[structure objectAtIndex:c] forKey:[hierarchy objectAtIndex:c]]; | |
} | |
NSLog(@"saving %@",[hierarchy objectAtIndex:c]); | |
} | |
return parent; | |
} |
NSString *remainder = [hierarchy subArrayWithRange: NSMakeRange(1, [hierarchy count] - 1)];
should probably be:
NSArray *remainder = [hierarchy subArrayWithRange: NSMakeRange(1, [hierarchy count] - 1)];
NSArray *remainder = [hierarchy subArrayWithRange: NSMakeRange(1, [hierarchy count] - 1)];
should be:
NSArray *remainder = [hierarchy subarrayWithRange: NSMakeRange(1, [hierarchy count] - 1)];
And dealing with NSMutableDicts it looks alike:
-(NSMutableDictionary*)editNestedDict:(NSMutableDictionary*)dict setObject:(id)object forKeyHierarchy:(NSArray*)hierarchy
{
NSMutableDictionary *newDict = [NSMutableDictionary dictionaryWithDictionary: dict];
if([hierarchy count] > 1)
{
NSString *key = [hierarchy objectAtIndex: 0];
NSArray *remainder = [hierarchy subarrayWithRange: NSMakeRange(1, [hierarchy count] - 1)];
NSMutableDictionary *subDict = [newDict objectForKey: key];
subDict = [self editNestedDict: subDict setObject: object forKeyHierarchy: remainder];
[newDict setObject: subDict forKey: key];
}
else
{
[newDict setObject: object forKey: [hierarchy lastObject]];
}
return newDict;
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A recursive version would look something like this: