Replace object at key path in NS[Mutable]Dictionary


Jonathan Taylor
 

HiI all,

I have a dictionary that has been loaded from a plist file, which has a deep structure of dictionaries within dictionaries. I would like to modify just one key-value pair deep in that hierarchy. Is there a neat off-the-shelf way of doing that?

I can load the plist file as an NSMutableDictionary, but it seems that only makes the top level dictionary mutable. The dictionaries-within-dictionaries are still immutable. Suppose I want to modify the value associated with the key path dict.next_level.deeper.even_deeper.key. The only way I can see to achieve this is to replicate “even_deeper" as a mutable dictionary, modify the value of “key” in it, replicate “deeper” and modify “even_deeper”, replicate “next_level” and modify “deeper”, and finally modify “next_level” within “dict”.

I could write recursive code to do that, but it feels as if there ought to be a more straightforward way of achieving what I want. Can anyone point me at something I have missed?

Cheers
Jonny.

[University of Glasgow: The Times Scottish University of the Year 2018]


 



On Jan 2, 2018, at 9:55 AM, Jonathan Taylor <jonathan.taylor@...> wrote:

I can load the plist file as an NSMutableDictionary, but it seems that only makes the top level dictionary mutable. The dictionaries-within-dictionaries are still immutable. 

Have you tried using NSPropertyListSerialization with the NSPropertyListMutableContainers option? That should make all the nested dictionaries/arrays mutable.

—Jens


Jon Gotow
 

On Jan 2, 2018, at 10:55 AM, Jonathan Taylor <jonathan.taylor@...> wrote:

I have a dictionary that has been loaded from a plist file, which has a deep structure of dictionaries within dictionaries. I would like to modify just one key-value pair deep in that hierarchy. Is there a neat off-the-shelf way of doing that?

I can load the plist file as an NSMutableDictionary, but it seems that only makes the top level dictionary mutable. The dictionaries-within-dictionaries are still immutable. Suppose I want to modify the value associated with the key path dict.next_level.deeper.even_deeper.key. The only way I can see to achieve this is to replicate “even_deeper" as a mutable dictionary, modify the value of “key” in it, replicate “deeper” and modify “even_deeper”, replicate “next_level” and modify “deeper”, and finally modify “next_level” within “dict”.

I could write recursive code to do that, but it feels as if there ought to be a more straightforward way of achieving what I want. Can anyone point me at something I have missed?
I've always just converted the entire dictionary and its contents to mutable objects. I've included some code below for the necessary categories to implement "mutableCopyDeep" methods. It does a deep traversal to create mutable copies of everything in an NSDictionary or NSArray.


- Jon



#import "MutableCopyDeep.h"

@implementation NSDictionary (MutableCopyDeep)

- (NSMutableDictionary*)mutableCopyDeep
{
NSMutableDictionary* copy = [[NSMutableDictionary alloc] init];
[self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
id cobj = [obj mutableCopyDeep];
[copy setObject:cobj forKey:key];
#if !__has_feature(objc_arc)
[cobj release];
#endif
}];

return copy;
}


@end

#pragma mark -

@implementation NSArray (MutableCopyDeep)

- (NSMutableArray*)mutableCopyDeep
{
NSMutableArray* copy = [[NSMutableArray alloc] init];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id cobj = [obj mutableCopyDeep];
[copy addObject:cobj];
#if !__has_feature(objc_arc)
[cobj release];
#endif
}];

return copy;
}

@end

#pragma mark -

@implementation NSObject (MutableCopyDeep)

- (id)mutableCopyDeep
{
if([self respondsToSelector:@selector(mutableCopyWithZone:)])
return [self mutableCopy];
else
return [self copy];
}


@end


Jonathan Taylor
 

I can load the plist file as an NSMutableDictionary, but it seems that only makes the top level dictionary mutable. The dictionaries-within-dictionaries are still immutable. 

Have you tried using NSPropertyListSerialization with the NSPropertyListMutableContainers option? That should make all the nested dictionaries/arrays mutable.

Thankyou Jens (and also Jon) for your reply. That was just what I was hoping for - an API that I didn’t even know where to look for (though obvious in retrospect!). That should do very nicely.