4

Ok, i kind of asked the wrong question so I've edited the original question.

I'm storing Arrays within Arrays, as well as NSDictionaries. It's a utility kind of application and there is no set structure, the user can enter nested information as much as they require.

Ideally I need a method to scroll through the entire contents of my array given a set parameter (a type of class, maybe a dictionary key). Here's an example..

NSMutableArray *array = [[NSMutableArray alloc]init];

NSMutableDictionary *enteredItem = [[NSMutableDictionary alloc]init];

[enteredItem setObject:@"i'm a title"       forKey:@"title"];
[enteredItem setObject:@"i'm an id"         forKey:@"id"];
[enteredItem setObject:@"i'm a description" forKey:@"description"];
[enteredItem setObject:@"i'm a timestamp"   forKey:@"timestamp"];
[enteredItem setObject:array                forKey:@"items"];


[array addObject:enteredItem];
[array addObject:anotherDictionary];
[array addObject:moreDictionaries];

So in the example above, I would need to find the dictionary (and return it) that contains @"i'm an id".

Hopefully my question is clear. Thanks for any help you can offer.

user1168056
  • 401
  • 8
  • 19

3 Answers3

4

Recursive approach is correct, but I'm not sure the code samples were very helpful if you don't already know recursion. Here's a working solution:

Add these methods:

- (id)findObjectWithKey:(id)key inArray:(NSArray *)array
{
    for (id object in array)
    {
        if ([object isKindOfClass:[NSArray class]])
        {
            return [self findObjectWithKey:key inArray:object];
        }
        else if ([object isKindOfClass:[NSDictionary class]])
        {
            return [self findObjectWithKey:key inDictionary:object];
        }
    }
    return nil;
}

- (id)findObjectWithKey:(id)key inDictionary:(NSDictionary *)dict
{
    for (id subKey in dict)
    {
        id object = [dict objectForKey:subKey];
        if ([subKey isEqual:key])
        {
            return object;
        }
        else if ([object isKindOfClass:[NSArray class]])
        {
            return [self findObjectWithKey:key inArray:object];
        }
        else if ([object isKindOfClass:[NSDictionary class]])
        {
            return [self findObjectWithKey:key inDictionary:object];
        }
    }
    return nil;
}

Then to find your object, just say:

id object = [self findObjectForKey:@"title" inArray:array];

To modify the methods to find a specific object and return the dictionary key, do this instead:

- (id)findKeyWithObject:(id)key inArray:(NSArray *)array
{
    for (id object in array)
    {
        if ([object isKindOfClass:[NSArray class]])
        {
            return [self findKeyWithObject:key inArray:object];
        }
        else if ([object isKindOfClass:[NSDictionary class]])
        {
            return [self findKeyWithObject:key inDictionary:object];
        }
    }
    return nil;
}

- (id)findKeyWithObject:(id)object inDictionary:(NSDictionary *)dict
{
    for (id key in dict)
    {
        id subObject = [dict objectForKey:key];
        if ([subObject isEqual:object])
        {
            return key;
        }
        else if ([subObject isKindOfClass:[NSArray class]])
        {
            return [self findKeyWithObject:object inArray:object];
        }
        else if ([subObject isKindOfClass:[NSDictionary class]])
        {
            return [self findKeyWithObject:object inDictionary:object];
        }
    }
    return nil;
}

Then to find your key, just say:

id key = [self findKeyWithObject:object inArray:array];
meaning-matters
  • 21,929
  • 10
  • 82
  • 142
Nick Lockwood
  • 40,865
  • 11
  • 112
  • 103
  • Thanks for the detailed explanation! I'll lt you know to see how it goes :) – user1168056 Jan 26 '12 at 13:12
  • Could this be modified to use the object in the dictionary as a search parameter? eg.. [self findKeyForObject@"i'm a user id"]; Cheers :) – user1168056 Jan 26 '12 at 14:19
  • Yes, it's possible - just compare the object instead of the key and return the key if the objects are equal. Everything else should be the same. Although if you found the object in the array, you couldn't return it's key (arrays don't have keys). – Nick Lockwood Jan 26 '12 at 14:34
  • Hi, having a little trouble changing things around to find an object instead of a key. I'm hard coding it for now. I'm currently doing.. if ([[dict objectForKey:@"id"]isEqualToString:@"3"]) { return dict; } But it's crashing. – user1168056 Jan 29 '12 at 18:24
  • I thought you were trying to find the key - why are you returning the dictionary? Anyway, I've updated the example above to include the inverse methods. Let me know if that wasn't what you meant. – Nick Lockwood Jan 29 '12 at 20:55
  • Very handy, thanks for sharing! I added a few `]` that were missing. – meaning-matters Jul 10 '13 at 06:30
1

How about a recursive search?

- (void) searchRecursive :(NSArray *) array {
    NSEnumerator *e = [array objectEnumerator];
    id obj;
    while ((obj = [e nextObject])) {
        if ([e isKindOfClass [NSArray class]])
            [self searchRecursive :obj]
        else
            objobject ... forKey ...
ott--
  • 5,642
  • 4
  • 24
  • 27
1

As ott says, a recursive approach would fit your requirements. You will also need to check if a dictionary contains the keys you want (because I guess you may have different types of objects represented by dictionaries...). Maybe you could create a NSDictionary category to check if it matches within a dictionary-class-type.

I mean:

- (void)isType1
{
 return (([self objectForKey:@"title"] != nil) && [self objectForKey:@"id"] != nil) && ...);
}

I know it could be more "sophisticated" but this may be useful.

Ricard Pérez del Campo
  • 2,387
  • 1
  • 19
  • 22