6

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.

2 Answers 2

13
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"
)
Sign up to request clarification or add additional context in comments.

2 Comments

Is that a replacement for `@"username != %@"? If so, thanks. But I would hate to use my method here.
Thanks a lot it works. You know you could have taken your time. I was banging my head throughout your edits. :P Thanks a lot though. :)
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.