zakdances
zakdances

Reputation: 23715

How to loop through a nested hierarchy of NSDictionaries and NSArrays and convert all to mutable copies?

I have an NSDictionary that contains instances of many different types objects (NSArrays, NSDictionaries, NSStrings, NSNumbers, etc...). Many of the NSDictionaries and NSStrings have their own nested NSDictionaries and NSArrays.

How can I loop through the entire hierarchy, from top to bottom, and convert ALL instances of NSDictionaries and NSArrays to NSMutableDictionaries and NSMutableArrays, respectively?

Is there any easy "recursively make mutable copies" function I'm unaware of? If not, do I just need to loop and type check repeatedly? Can I just replace as I go or do I have rebuild the entire hierarchy?

Upvotes: 8

Views: 2745

Answers (4)

gnasher729
gnasher729

Reputation: 52592

If you used some operating system method to create the dictionary: These methods often have a parameter that allows you to create mutable objects. For example, the JSON serialiser class has flags to make all dictionaries and arrays mutable, and to make all NSString objects mutable (NSNumber and NSNull are never mutable).

Upvotes: 1

Martin R
Martin R

Reputation: 539945

The following method creates a nested (deep) mutable copy of nested arrays, dictionaries and sets. It can also be used to create mutable copies of non-collection objects inside the hierarchy, such as strings.

@interface NSObject (MyDeepCopy)
-(id)deepMutableCopy;
@end

@implementation  NSObject (MyDeepCopy)
-(id)deepMutableCopy
{
    if ([self isKindOfClass:[NSArray class]]) {
        NSArray *oldArray = (NSArray *)self;
        NSMutableArray *newArray = [NSMutableArray array];
        for (id obj in oldArray) {
            [newArray addObject:[obj deepMutableCopy]];
        }
        return newArray;
    } else if ([self isKindOfClass:[NSDictionary class]]) {
        NSDictionary *oldDict = (NSDictionary *)self;
        NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
        for (id obj in oldDict) {
            [newDict setObject:[oldDict[obj] deepMutableCopy] forKey:obj];
        }
        return newDict;
    } else if ([self isKindOfClass:[NSSet class]]) {
        NSSet *oldSet = (NSSet *)self;
        NSMutableSet *newSet = [NSMutableSet set];
        for (id obj in oldSet) {
            [newSet addObject:[obj deepMutableCopy]];
        }
        return newSet;
#if MAKE_MUTABLE_COPIES_OF_NONCOLLECTION_OBJECTS
    } else if ([self conformsToProtocol:@protocol(NSMutableCopying)]) {
            // e.g. NSString
        return [self mutableCopy];
    } else if ([self conformsToProtocol:@protocol(NSCopying)]) {
            // e.g. NSNumber
        return [self copy];
#endif
    } else {
        return self;
    }
}
@end

Use it like

NSDictionary *dict = ...;
NSMutableDictionary *mdict = [dict deepMutableCopy];

(Dictionary keys are not copied, only the values).

I am quite sure that I have seen something like this on SO, but cannot find it right now.

Upvotes: 18

bbum
bbum

Reputation: 162722

While Anoop's answer is just fine (I think, I didn't compile it either), if your hierarchy of instances is actually a property list, then you can use NSPropertyListSerialization to deserialize a plist with mutable containers and/or leaf nodes.

This would reduce to fewer lines of code and likely be faster than a manual descent through the object graph, but this solution only really makes sense if you are initially deserializing the plist from somewhere.

Upvotes: 2

Anoop Vaidya
Anoop Vaidya

Reputation: 46543

For the outer NSDictionary, this will create for 1-level down.

You need to call this in a method, and go on to get all dict and array.

NSMutableDictionary *dict=[NSMutableDictionary new];
NSMutableArray *array=[NSMutableArray new];

for(NSDictionary *dict in yourMainDictionary){
   if([outerDict[dict] class]==[NSDictionary class]){
      [dict setObject:outerDict[dict] forKey:dict];
   }
   else if([outerDict[dict] class]==[NSArray class]){
      array[array.count]=outerDict[dict];
   }
}

Not compiler checked. Read as algorithm

Upvotes: 1

Related Questions