1

I'm looping through an array and comparing the objects tag property in this array with the objects in another array.

Here's my code:

NSArray *objectsArray = ...;
NSArray *anotherObjectArray = ...;
NSMutableArray *mutableArray = ...;

for (ObjectA *objectA in objectsArray) {
    for (ObjectZ *objectZ in anotherObjectArray) {
        if ([objectA.tag isEqualToString:objectZ.tag]) {
            [mutableArray addObject:objectA];
        }
    }
}

Is there a better way to do this?

Please note the tag property is not an integer, so have to compare strings.

runmad
  • 14,846
  • 9
  • 99
  • 140
  • 3
    Sorry, but where is objectA being used? – mackworth Sep 26 '11 at 16:32
  • 1
    Or, similarly, what is `childParent`? – Jim Puls Sep 26 '11 at 16:34
  • Oh, and one more: why do you want to optimize? Is this code getting executed a lot, or are there a lot of elements in objectsArray or [childParent children] or both? – mackworth Sep 26 '11 at 16:49
  • Edited the questions to make it clear and give proper names for the arrays. Sorry about that. – runmad Sep 26 '11 at 16:49
  • @mackworth: Just wondering if there's a better way of doing this? Since I am basically looping through anotherObjectArray for every single object inside the first fast-enumeration for objectsArray. – runmad Sep 26 '11 at 16:50

4 Answers4

5

You can do this by iterating over each array once, rather than nesting:

NSMutableSet *tagSet = [NSMutableSet setWithCapacity:[anotherObjectArray count]];

for(ObjectZ *objectZ in antherObjectArray) {
    [tagSet addObject:objectZ.tag];
}

NSMutableArray *output = [NSMutableArray mutableArray];

for(ObjectA *objectA in objectsArray) {
    if([tagSet containsObject:objectA.tag]) {
        [output addObject:objectA];
    }
}
Seamus Campbell
  • 17,816
  • 3
  • 52
  • 60
  • But, doesn't containsObject (and indexOfObject) just internally iterate across the set or array anyways? – mackworth Sep 26 '11 at 17:05
  • indexOfObject has to iterate over the array, but containsObject is constant time. – Seamus Campbell Sep 26 '11 at 17:41
  • Dude, you're awesome. This cut my parsing time down by 50% (obviously this was a major loop). Very clever! – runmad Sep 26 '11 at 17:47
  • 1
    Actually, on an iPhone 4, I'm seeing 40 seconds instead of 141 seconds, so results are much improved on the actual device. Cheers again! – runmad Sep 26 '11 at 17:48
  • Remove the first `for` loop and instead do `NSSet *tagSet = [NSSet setWithArray:anotherObjectArray];`, that should be faster. – DarkDust Sep 26 '11 at 17:57
  • He wants to test the .tag property, not the object itself. Note that the objects in the two arrays have different types. – Seamus Campbell Sep 26 '11 at 18:59
1

Well, the simplest change (as there can only be one match per objectA) then you could do a break after your [mutableArray addObject:objectA]. When a match occurs, that would reduce the inner loop by 50%.

More dramatically, if you're doing this a lot and the order of anotherObjectArray doesn't matter, would be to invert your anotherObjectArray data structure and use a dictionary, storing the objects by tag. Then you just iterate over objectA asking if its tag is in the dictionary of ObjectZs.

mackworth
  • 5,873
  • 2
  • 29
  • 49
1

May be you can use [NSArray filteredArrayUsingPredicate:]; - http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html

But you may have to tweak for property tag yourself.

NSArray *objectsArray = [NSArray arrayWithObjects:@"Miguel", @"Ben", @"Adam", @"Melissa", nil];
NSArray *tagsArray = [NSArray arrayWithObjects:@"Miguel", @"Adam", nil];

NSPredicate *sPredicate = [NSPredicate predicateWithFormat:@"SELF IN %@", tagsArray];
NSArray *results = [objectsArray filteredArrayUsingPredicate:sPredicate];
NSLog(@"Matched %d", [results count]);
for (id a in results) {
    NSLog(@"Object is %@", a);
}

Hope this helps

Jirapong
  • 24,074
  • 10
  • 54
  • 72
0

Thanks for all the answers. While I have accepted the NSMutableSet solution, I actually ended up going with the following, as it turned out it was a tiny bit faster:

NSMutableDictionary *tagDictionary = [NSMutableDictionary dictionaryWithCapacity:[anotherObjectArray count]];
for (ObjectZ *objectZ in anotherObjectArray) {
    [tagDictionary setObject:objectZ.tag forKey:objectZ.tag];
    }
for (ObjectA *objectA in objectsArray) {
    if ([tagDictionary objectForKey:objectA.tag]) {
        [direction addObject:objectA];
    }
}
runmad
  • 14,846
  • 9
  • 99
  • 140