53

How to search an NSSet or NSArray for an object which has an specific value for an specific property?

Example: I have an NSSet with 20 objects, and every object has an type property. I want to get the first object which has [theObject.type isEqualToString:@"standard"].

I remember that it was possible to use predicates somehow for this kind of stuff, right?

dontWatchMyProfile
  • 45,440
  • 50
  • 177
  • 260

4 Answers4

81
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"type == %@", @"standard"];
NSArray *filteredArray = [myArray filteredArrayUsingPredicate:predicate];
id firstFoundObject = nil;
firstFoundObject =  filteredArray.count > 0 ? filteredArray.firstObject : nil;

NB: The notion of the first found object in an NSSet makes no sense since the order of the objects in a set is undefined.

Shmidt
  • 16,436
  • 18
  • 88
  • 136
Ole Begemann
  • 135,006
  • 31
  • 278
  • 256
  • Does it work if the object doesn't have the 'type' property, but has another object (let's say 'job'), and this is 'job' which has the 'type' property? – darksider Oct 09 '12 at 08:27
  • Yes, but a class must be key-value coding compliant for the keys you want to use in a predicate. – Daniel Wood Nov 09 '12 at 09:33
  • `indexOfObjectPassingTest` seems better for two reasons: (1) You use an actual method rather than a *string* (2) You stop on the "first" element you find - it doesn't matter that a set is not ordered, you may still want just one item. – Ohad Schneider Nov 13 '14 at 14:56
17

You can get the filtered array as Jason and Ole have described, but since you just want one object, I'd use - indexOfObjectPassingTest: (if it's in an array) or -objectPassingTest: (if it's in a set) and avoid creating the second array.

NSResponder
  • 16,861
  • 7
  • 32
  • 46
  • 1
    That seems a good idea for an NSArray, but the method in NSSet is actually `-objectsPassingTest:` (note the plural), so this idea isn't directly possible there, if I understand this correctly. – Ashley Jun 14 '13 at 14:07
15

Generally, I use indexOfObjectPassingTest: as I find it more convenient to express my test in Objective-C code rather than NSPredicate syntax. Here's a simple example (imagine that integerValue was actually a property):

NSArray *array = @[@0,@1,@2,@3];
NSUInteger indexOfTwo = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return ([(NSNumber *)obj integerValue] == 2);
}];
NSUInteger indexOfFour = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return ([(NSNumber *)obj integerValue] == 4);
}];
BOOL hasTwo = (indexOfTwo != NSNotFound);
BOOL hasFour = (indexOfFour != NSNotFound);
NSLog(@"hasTwo: %@ (index was %d)", hasTwo ? @"YES" : @"NO", indexOfTwo);
NSLog(@"hasFour: %@ (index was %d)", hasFour ? @"YES" : @"NO", indexOfFour);

The output of this code is:

hasTwo: YES (index was 2)
hasFour: NO (index was 2147483647)
Carl Veazey
  • 18,392
  • 8
  • 66
  • 81
5
NSArray* results = [theFullArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF.type LIKE[cd] %@", @"standard"]];
Jason Coco
  • 77,985
  • 20
  • 184
  • 180