40

I have three NSMutableArray containing names that are added to the lists according to different criterieas.

Here are my arrays pseudocode:

NSMutableArray *array1 = [@"Jack", @"John", @"Daniel", @"Lisa"];
NSMutableArray *array2 = [@"Jack", @"Bryan", @"Barney", @"Lisa",@"Penelope",@"Angelica"];
NSMutableArray *array3 = [@"Jack", @"Jerome", @"Dan", @"Lindsay", @"Lisa"];

I want to find a fourth array which includes the intersection of those three arrays. In this case for example it will be:

NSMutableArray *array4 = [@"Jack",@"Lisa"];

Because all the three array have jack and lisa as an element. Is there way of simply doing this?

aeciftci
  • 679
  • 2
  • 6
  • 15

5 Answers5

74

Use NSMutableSet:

NSMutableSet *intersection = [NSMutableSet setWithArray:array1];
[intersection intersectSet:[NSSet setWithArray:array2]];
[intersection intersectSet:[NSSet setWithArray:array3]];

NSArray *array4 = [intersection allObjects];

The only issue with this is that you lose ordering of elements, but I think (in this case) that that's OK.


As has been pointed out in the comments (thanks, Q80!), iOS 5 and OS X 10.7 added a new class called NSOrderedSet (with a Mutable subclass) that allows you to perform these same intersection operations while still maintaining order.

Community
  • 1
  • 1
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • I was wondering how will I intersect these `NSMutableArray`s if these are array of custom objects? eg: `NSMutableArray *array1 = [obj1, obj2, obj3, obj4];` and so on... – geekay May 11 '12 at 17:25
  • 2
    This will work as long as the arrays don't contain duplicates themselves (the intersect for `array1 = [obj1, obj2, obj2, obj3]` and `array2 = [obj2, obj2, obj3, obj4]` should be `intersect = [obj2, obj2, obj3]`). If the arrays contain duplicates or the ordering is important then it's better to filter the arrays using predicates. – David Rönnqvist Aug 03 '12 at 16:05
  • 1
    @geekay_gk It will use the methods `hash` and `isEqual:` to determine what objects exist in both collections – David Rönnqvist Aug 03 '12 at 16:07
  • 1
    It's kind of confusing for a base class to have a read-only method that returns a boolean and a subclass to have a void method with the same name that modifies the object. – Eric Walker Nov 18 '13 at 00:15
  • @EricWalker what do you mean? `intersectsSet` is not the same thing as `intersectSet:`. – Dave DeLong Nov 18 '13 at 00:28
  • 3
    If you want to keep the order, use `NSMutableOrderedSet`. Something similar to : `NSMutableOrderedSet *ordered = [NSMutableOrderedSet orderedSetWithArray:array1];`. Then intersect, etc. etc. – Q8i Feb 25 '14 at 15:37
  • How well does this handle large arrays? What is the arrays have thousands of objects in it? Does this scale well? – zumzum Jul 15 '14 at 04:20
  • 1
    The example code will not yield expected result as you are intersecting A with B and then the result of that intersection with C; ((A.intersect B).intersect C). What you awnt to do is to intersect each array with the union of all other arrays; (A.intersect (B union C)) + (B.intersect (A union C)) + (C.intersect (B union C)) – Voxar Aug 30 '14 at 22:18
2

This is cleaner than the NSSet approach, and you won't lose the original ordering.

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self IN %@ AND self IN %@", array2, array3];
NSArray *array4 = [array1 filteredArrayUsingPredicate:predicate];
trapper
  • 11,716
  • 7
  • 38
  • 82
2

Have a look at this post.

In short: if you can use NSSet instead of NSArray, then it's trivial (NSMutableSet has intersectSet:).

Otherwise, you can build an NSSet from your NSArray and go back to the above case.

Community
  • 1
  • 1
sergio
  • 68,819
  • 11
  • 102
  • 123
1
NSMutableArray *first = [[NSMutableArray alloc] initWithObjects:@"Jack", @"John", @"Daniel", @"Lisa",nil];

NSMutableArray *seconds =[[NSMutableArray alloc] initWithObjects:@"Jack", @"Bryan", @"Barney", @"Lisa",@"Penelope",@"Angelica",nil];

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


for (id obj in first) {

    if ([seconds  containsObject:obj] ) {


        [third addObject:obj];

    }


}


NSLog(@"third is : %@ \n\n",third);

OUTPUT:

third is : (

Jack,

Lisa

)

-1

Here is a working variant from links above

NSPredicate *intersectPredicate = [NSPredicate predicateWithFormat:@"SELF IN %@", @[@500, @400, @600]];
NSArray *intersect = [@[@200, @300, @400] filteredArrayUsingPredicate:intersectPredicate];
Hans Roerdinkholder
  • 3,000
  • 1
  • 20
  • 30
  • You are using your answer to both attempt to answer the question and to comment on other answers. Please update your answer to only answer the question and comment on the appropriate answers if you wish to respond to them. You answer is hard to comprehend by itself as it is. – Hans Roerdinkholder Oct 17 '14 at 11:13