HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Fetch plist file

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
filefetchplist

Problem

I have a plist file with many nested dictionaries inside of it, and I wanna fetch them all to my code. Isn't there any more efficient way to do that than nested for-loops?

```
NSArray * keys = [plistDictionary allKeys];
for (NSString * key in keys) {
// Level 1
NSLog(@"%@", key );

BOOL isDictionary = [[plistDictionary objectForKey:key] isKindOfClass:[NSDictionary class]];
if (isDictionary) {
NSArray * keys2 = [[plistDictionary objectForKey:key] allKeys];
for (NSString * key2 in keys2) {
// Level 2
NSLog(@"%@ : %@", key, key2 );

BOOL isDictionary = [[[plistDictionary objectForKey:key] objectForKey:key2] isKindOfClass:[NSDictionary class]];
if (isDictionary) {
// Level 3
NSArray *keys3 = [[[plistDictionary objectForKey:key] objectForKey:key2] allKeys];
for (NSString *key3 in keys3) {
NSLog(@"%@ : %@ : %@", key, key2, key3 );

BOOL isDictionary = [[[[plistDictionary objectForKey:key] objectForKey:key2] objectForKey:key3] isKindOfClass:[NSDictionary class]];
if (isDictionary) {
// Level 4
NSArray *keys4 = [[[[plistDictionary objectForKey:key] objectForKey:key2] objectForKey:key3] allKeys];
for (NSString * key4 in keys4) {
NSLog(@ "%@ : %@ : %@ : %@",key, key2, key3, key4 );

BOOL isDictionary = [[[[[plistDictionary objectForKey:key] objectForKey:key2] objectForKey:key3] objectForKey:key4] isKindOfClass:[NSDictionary class]];
if (isDictionary) {
// Level 5
NSArray * keys5 = [[[[[plistDictionary objectForKey:key] objectForKey:key2] objectForKey:key3] objectForKey:key4] allKeys];
for (NSString * key5 in keys5) {

Solution

The first thing you can do is to store the intermediate objects in variables,
in order to get rid of the repeated and deeply nested objectForKey: chains
like

NSArray *keys4 = [[[[plistDictionary objectForKey:key] objectForKey:key2] objectForKey:key3] allKeys];


Then it would look like this:

NSArray * keys = [plistDictionary allKeys];
for (NSString * key in keys) {
    // Level 1
    NSLog(@"%@", key );
    NSDictionary *obj1 = plistDictionary[key];
    if ([obj1 isKindOfClass:[NSDictionary class]]) {
        NSArray * keys2 = [obj1 allKeys];
        for (NSString * key2 in keys2) {
            // Level 2
            NSLog(@"%@ : %@", key, key2 );
            NSDictionary *obj2 = obj1[key2];
            if ([obj2 isKindOfClass:[NSDictionary class]]) {
                // Level 3
                NSArray *keys3 = [obj2 allKeys];
                for (NSString *key3 in keys3) {
                    NSLog(@"%@ : %@ : %@", key, key2, key3 );
                    NSDictionary *obj3 = obj2[key3];
                    // ...
                }
            }
        }
    }
}


Note also the use of the dictionary subscripting syntax instead of objectForKey:.

This can be simplified further by using the enumerateKeysAndObjectsUsingBlock:
method of NSDictionary. This method calls the given block once for each
key and the corresponding value:

[plistDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key1, NSDictionary *obj1, BOOL *stop) {
    NSLog(@"%@", key1);
    if ([obj1 isKindOfClass:[NSDictionary class]]) {
        [obj1 enumerateKeysAndObjectsUsingBlock:^(NSString *key2, NSDictionary *obj2, BOOL *stop) {
            NSLog(@"%@ : %@", key1, key2);
            if ([obj2 isKindOfClass:[NSDictionary class]]) {
                [obj2 enumerateKeysAndObjectsUsingBlock:^(NSString *key3, NSDictionary *obj3, BOOL *stop) {
                    NSLog(@"%@ : %@ : %@", key1, key2, key3);
                    if ([obj3 isKindOfClass:[NSDictionary class]]) {
                        [obj3 enumerateKeysAndObjectsUsingBlock:^(NSString *key4, NSDictionary *obj4, BOOL *stop) {
                            NSLog(@ "%@ : %@ : %@ : %@", key1, key2, key3, key4);
                            if ([obj4 isKindOfClass:[NSDictionary class]]) {
                                [obj4 enumerateKeysAndObjectsUsingBlock:^(NSString *key5, NSDictionary *obj5, BOOL *stop) {
                                    NSLog(@"%@ : %@ : %@ : %@ : %@", key1, key2, key3, key4, key5);
                                }];
                            }
                       }];
                    }
                }];
            }
        }];
    }
}];


This is still a lot of repeated code, and can be further simplified using
recursion:

-(void)printPlist:(NSDictionary *)plistDictionary previousKeys:(NSArray *)previousKeys {
    [plistDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSDictionary *obj, BOOL *stop) {
        NSArray *newKeys = [previousKeys arrayByAddingObject:key];
        NSLog(@"%@", [newKeys componentsJoinedByString:@" : "]);
        if ([obj isKindOfClass:[NSDictionary class]]) {
            [self printPlist:obj previousKeys:newKeys];
        }
    }];
}

-(void)printPlist:(NSDictionary *)plistDictionary {
    [self printPlist:plistDictionary previousKeys:@[]];
}


But what if the plist has not only dictionaries but also arrays as "nested structures"
(array in dictionary, dictionary in array, array in array, ...)?
That is not too complicated in the recursive version, we only have to test for
dictionary or array type. The function now takes an generic id parameter
and then enumerates the dict or array and calls itself recursively. We can also
print the "terminal nodes" or "values", i.e. everything that is not a collection
(strings, numbers, ...):

-(void)printPlist:(id)plistObject previousKeys:(NSArray *)previousKeys {
    if ([plistObject isKindOfClass:[NSDictionary class]]) {
        // Enumerate dictionary:
        [(NSDictionary *)plistObject enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
            NSArray *newKeys = [previousKeys arrayByAddingObject:key];
            NSLog(@"%@", [newKeys componentsJoinedByString:@" : "]);
            [self printPlist:obj previousKeys:newKeys];
        }];
    } else if ([plistObject isKindOfClass:[NSArray class]]) {
        // Enumerate array:
        [(NSArray *)plistObject enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSArray *newKeys = [previousKeys arrayByAddingObject:@(idx)];
            NSLog(@"%@", [newKeys componentsJoinedByString:@" : "]);
            [self printPlist:obj previousKeys:newKeys];
        }];
    } else {
        // Not a collection, print keys and value:
        NSLog(@"%@ = %@", [previousKeys componentsJoinedByString:@" : "], plistObject);
    }
}

-(void)printPlist:(id)plistObject {
    [self printPlist:plistObject previousKeys:@[]];
}


That works, but it doe

Code Snippets

NSArray *keys4 = [[[[plistDictionary objectForKey:key] objectForKey:key2] objectForKey:key3] allKeys];
NSArray * keys = [plistDictionary allKeys];
for (NSString * key in keys) {
    // Level 1
    NSLog(@"%@", key );
    NSDictionary *obj1 = plistDictionary[key];
    if ([obj1 isKindOfClass:[NSDictionary class]]) {
        NSArray * keys2 = [obj1 allKeys];
        for (NSString * key2 in keys2) {
            // Level 2
            NSLog(@"%@ : %@", key, key2 );
            NSDictionary *obj2 = obj1[key2];
            if ([obj2 isKindOfClass:[NSDictionary class]]) {
                // Level 3
                NSArray *keys3 = [obj2 allKeys];
                for (NSString *key3 in keys3) {
                    NSLog(@"%@ : %@ : %@", key, key2, key3 );
                    NSDictionary *obj3 = obj2[key3];
                    // ...
                }
            }
        }
    }
}
[plistDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key1, NSDictionary *obj1, BOOL *stop) {
    NSLog(@"%@", key1);
    if ([obj1 isKindOfClass:[NSDictionary class]]) {
        [obj1 enumerateKeysAndObjectsUsingBlock:^(NSString *key2, NSDictionary *obj2, BOOL *stop) {
            NSLog(@"%@ : %@", key1, key2);
            if ([obj2 isKindOfClass:[NSDictionary class]]) {
                [obj2 enumerateKeysAndObjectsUsingBlock:^(NSString *key3, NSDictionary *obj3, BOOL *stop) {
                    NSLog(@"%@ : %@ : %@", key1, key2, key3);
                    if ([obj3 isKindOfClass:[NSDictionary class]]) {
                        [obj3 enumerateKeysAndObjectsUsingBlock:^(NSString *key4, NSDictionary *obj4, BOOL *stop) {
                            NSLog(@ "%@ : %@ : %@ : %@", key1, key2, key3, key4);
                            if ([obj4 isKindOfClass:[NSDictionary class]]) {
                                [obj4 enumerateKeysAndObjectsUsingBlock:^(NSString *key5, NSDictionary *obj5, BOOL *stop) {
                                    NSLog(@"%@ : %@ : %@ : %@ : %@", key1, key2, key3, key4, key5);
                                }];
                            }
                       }];
                    }
                }];
            }
        }];
    }
}];
-(void)printPlist:(NSDictionary *)plistDictionary previousKeys:(NSArray *)previousKeys {
    [plistDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSDictionary *obj, BOOL *stop) {
        NSArray *newKeys = [previousKeys arrayByAddingObject:key];
        NSLog(@"%@", [newKeys componentsJoinedByString:@" : "]);
        if ([obj isKindOfClass:[NSDictionary class]]) {
            [self printPlist:obj previousKeys:newKeys];
        }
    }];
}

-(void)printPlist:(NSDictionary *)plistDictionary {
    [self printPlist:plistDictionary previousKeys:@[]];
}
-(void)printPlist:(id)plistObject previousKeys:(NSArray *)previousKeys {
    if ([plistObject isKindOfClass:[NSDictionary class]]) {
        // Enumerate dictionary:
        [(NSDictionary *)plistObject enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
            NSArray *newKeys = [previousKeys arrayByAddingObject:key];
            NSLog(@"%@", [newKeys componentsJoinedByString:@" : "]);
            [self printPlist:obj previousKeys:newKeys];
        }];
    } else if ([plistObject isKindOfClass:[NSArray class]]) {
        // Enumerate array:
        [(NSArray *)plistObject enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSArray *newKeys = [previousKeys arrayByAddingObject:@(idx)];
            NSLog(@"%@", [newKeys componentsJoinedByString:@" : "]);
            [self printPlist:obj previousKeys:newKeys];
        }];
    } else {
        // Not a collection, print keys and value:
        NSLog(@"%@ = %@", [previousKeys componentsJoinedByString:@" : "], plistObject);
    }
}

-(void)printPlist:(id)plistObject {
    [self printPlist:plistObject previousKeys:@[]];
}

Context

StackExchange Code Review Q#68340, answer score: 5

Revisions (0)

No revisions yet.