简体   繁体   中英

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 , ie

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.

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"not (self.username IN %@)", [some_usernames valueForKey:@"username"]];
NSArray *remaining_usernames = [all_usernames filteredArrayUsingPredicate:predicate];

complete example

@interface Alpha : NSObject
@property (nonatomic, copy) NSString *username;
-(instancetype) initWithUsername:(NSString *)username;
@end

@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];
}
@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"]];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"not (self.username  IN %@)", [some_usernames valueForKey:@"username"]];
NSArray *remaining_usernames = [all_usernames filteredArrayUsingPredicate:predicate];

NSLog(@"%@", remaining_usernames);

prints

(
    "Alpha: a",
    "Alpha: z",
    "Alpha: e"
)

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM