user2716667
user2716667

Reputation: 15

Joining two arrays of objects on a common key

I have two NSMutable Arrays (5,000 records each at least) that I would like to join into a single array/dictionary. Both arrays have a common key=>value. I would like a single array that would contain keys/values from both arrays. I have seen some examples in PHP, (see Joining two multidimensional arrays using common array value) but I am looking for Objective C solution.

NSMutableArray #1:
{
    [0] => NSObject
    {
        [itemID] => 221
        [Name] => "Adam"
    }
    [1] => NSObject
    {
        [itemID] => 7
        [Name] => "Jacob"
    }
}

NSMutableArray #2:
{
    [0] => NSObject
    {
        [itemID] => 221
        [location] => floor 1
        [room] => 56
    }

    [1] => NSObject
    {
        [itemID] => 7
        [location] => floor 2
        [room] => 12
    }
}

What I am looking for:

NSMutableArray
{
    [0] => NSObject
    {
        [itemID] => 221
        [Name] => "Adam"
        [location] => floor 1
        [room] => 56
    }

    [1] => NSObject
    {
        [itemID] => 7
        [Name] => "Jacob" 
        [location] => floor 2
        [room] => 12
    }
}

Thanks for your advice:)

Upvotes: 0

Views: 1268

Answers (1)

foundry
foundry

Reputation: 31745

This won't be the most efficient, but should work...

  NSMutableArray* result = [[NSMutableArray alloc] init];
  for (NSDictionary* dict1 in array1) {
    for (NSDictionary* dict2 in array2) {
      if ([[dict1 objectForKey:@"itemID"] isEqual:
          [dict2 objectForKey:@"itemID"]]) {
             NSMutableDictionary* dict = [dict1 mutableCopy];
             [dict addEntriesFromDictionary:dict2];
             [result addObject: dict];
             break;
          }
    }
  }

update

Here are a few objectively-C ways to improve efficiency...

(1) sort your arrays before comparing them:

- (NSArray*)sortArray:(NSArray*)array {
    NSArray* sortDescriptors = 
         @[[[NSSortDescriptor alloc] initWithKey:@"itemID" 
                                       ascending:YES]];
    NSArray* result = [array sortedArrayUsingDescriptors:sortDescriptors];
    return result;
}

(2) reverse iterate through your dictionaries. Then remove the end of the inner array whenever you get a match (this is reasonable given that the arrays are sorted, and removing objects from the end of an array should be more efficient than removing from the start).

for (NSDictionary* dict1 in [sortedArray1 reverseObjectEnumerator]) {
    for (NSDictionary* dict2 in [sortedArray2 reverseObjectEnumerator]) {
        if ([[dict1 objectForKey:@"itemID"] isEqual:
             [dict2 objectForKey:@"itemID"]]) {
                NSMutableDictionary* dict = [dict1 mutableCopy];
                [dict addEntriesFromDictionary:dict2];
                [result addObject: dict];
                NSUInteger idx = [sortedArray2 indexOfObject:dict2];
                NSRange range= NSMakeRange(idx, sortedArray2.count-1-idx);
                [sortedArray2 removeObjectsInRange:range];
                break;
        }
    }
}

(3) use block-based fast enumeration:

[sortedArray1 enumerateObjectsWithOptions:NSEnumerationReverse
                               usingBlock:
     ^(NSDictionary* dict1, NSUInteger idx, BOOL *stop) {
         [sortedArray2 enumerateObjectsWithOptions:NSEnumerationReverse
                                        usingBlock:
          ^(NSDictionary* dict2, NSUInteger jdx, BOOL *stop) {
              if ([[dict1 objectForKey:@"itemID"] isEqual:
                   [dict2 objectForKey:@"itemID"]]) {
                      NSMutableDictionary* dict = [dict1 mutableCopy];
                      [dict addEntriesFromDictionary:dict2];
                      [result addObject: dict];
                      NSRange range= NSMakeRange(jdx, sortedArray2.count-1-jdx);
                      [sortedArray2 removeObjectsInRange:range];
                      *stop = YES;
              }
          }];
     }];

There may be negligible computational difference between (2) and (3), but (3) has the advantage of passing in the index and the object on each iteration, whereas in (2) you need to obtain the index using indexOfObject.

If you are sure that each array has the same set of comparison keys values, you can dispense with NSRange and simplify the inner array reduction to:

[sortedArray2 removeLastObject];

finally...

If, as your question suggests, you know in advance that your data is sorted and that there is a one-to-one correspondence between the respective array objects, you can simplify:

[arrayA enumerateObjectsUsingBlock:
    ^(NSDictionary* dict, NSUInteger idx, BOOL *stop) {
        NSMutableDictionary* mutableDict = [dict mutableCopy];
        [mutableDict addEntriesFromDictionary:arrayB[idx]];
         [result addObject:mutableDict];
    }];

Upvotes: 2

Related Questions