Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filter NSArray based on another array using predicate

Consider the arrays below. The arrays contain objects of type 'Alpha'. We only care about the property username which is of type NSString.

NSArray *some_usernames = @[ <multiple values of type Alpha> ]
NSArray *all_usernames = @[ <multiple values of type Alpha> ]

I basically want a list of all the usernames that are not in the array some_usernames, i.e.

NSArray *remaining_usernames = @[ <all_usernames but not in some_usernames> ];

The way I would intend to do is:

NSPredicates *predicates;
for (Alpha *alpha in some_usernames)
{
    predicate = [predicate with @"username != %@", alpha.username];
    predicates.add(predicate)
}

create compound predicate
filter all_usernames

But this feels like a bad way of doing this. Is there a way to do this in two lines? I have seen it before but I can't point to the code reference anymore.

like image 509
p0lAris Avatar asked Jan 07 '15 02:01

p0lAris


1 Answers

I want to add another answer:

If the ordering of the objects isn't needed (and — most likely — equal objects unwanted) you could instead of using predicate filtering on arrays use Sets and set arithmetic. To do so we must teach Alpha what equality means and provide a hash method. In this case we just use NSStrings implementation:

@implementation Alpha
-(instancetype) initWithUsername:(NSString *)username
{
    self = [super init];
    if (self) {
        self.username = username;
    }
    return self;
}

-(NSString *)description{
    return [NSString stringWithFormat:@"%@: %@", NSStringFromClass([self class]), self.username];
}

-(BOOL)isEqual:(id)object
{
    return [self.username isEqual:[object username]];
}

-(NSUInteger)hash
{
    return [self.username hash];
}

@end



NSArray *all_usernames = @[[[Alpha alloc] initWithUsername:@"a"],
                           [[Alpha alloc] initWithUsername:@"b"],
                           [[Alpha alloc] initWithUsername:@"z"],
                           [[Alpha alloc] initWithUsername:@"f"],
                           [[Alpha alloc] initWithUsername:@"e"]];

NSArray *some_usernames = @[[[Alpha alloc] initWithUsername:@"b"],
                            [[Alpha alloc] initWithUsername:@"f"]];

NSSet *allSet = [NSSet setWithArray:all_usernames];
NSSet *someSet = [NSSet setWithArray:some_usernames];

NSMutableSet *remainingSet = [allSet mutableCopy];
[remainingSet minusSet:someSet];

NSLog(@"%@", remainingSet);

prints

{(
    Alpha: z,
    Alpha: e,
    Alpha: a
)}

This code should be much faster for more data. Please watch WWDC 2013: Designing Code for Performance

like image 79
vikingosegundo Avatar answered Nov 03 '22 01:11

vikingosegundo