filtering NSArray into a new NSArray in Objective-C

Objective CCocoaNsarray

Objective C Problem Overview


I have an NSArray and I'd like to create a new NSArray with objects from the original array that meet certain criteria. The criteria is decided by a function that returns a BOOL.

I can create an NSMutableArray, iterate through the source array and copy over the objects that the filter function accepts and then create an immutable version of it.

Is there a better way?

Objective C Solutions


Solution 1 - Objective C

NSArray and NSMutableArray provide methods to filter array contents. NSArray provides filteredArrayUsingPredicate: which returns a new array containing objects in the receiver that match the specified predicate. NSMutableArray adds filterUsingPredicate: which evaluates the receiver’s content against the specified predicate and leaves only objects that match. These methods are illustrated in the following example.

NSMutableArray *array =
    [NSMutableArray arrayWithObjects:@"Bill", @"Ben", @"Chris", @"Melissa", nil];
 
NSPredicate *bPredicate =
    [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
NSArray *beginWithB =
    [array filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { @"Bill", @"Ben" }.
 
NSPredicate *sPredicate =
    [NSPredicate predicateWithFormat:@"SELF contains[c] 's'"];
[array filteredArrayUsingPredicate:sPredicate];
// array now contains { @"Chris", @"Melissa" }

Solution 2 - Objective C

There are loads of ways to do this, but by far the neatest is surely using [NSPredicate predicateWithBlock:]:

NSArray *filteredArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
    return [object shouldIKeepYou];  // Return YES for each object you want in filteredArray.
}]];

I think that's about as concise as it gets.


Swift:

For those working with NSArrays in Swift, you may prefer this even more concise version:

let filteredArray = array.filter { $0.shouldIKeepYou() }

filter is just a method on Array (NSArray is implicitly bridged to Swift’s Array). It takes one argument: a closure that takes one object in the array and returns a Bool. In your closure, just return true for any objects you want in the filtered array.

Solution 3 - Objective C

Based on an answer by Clay Bridges, here is an example of filtering using blocks (change yourArray to your array variable name and testFunc to the name of your testing function):

yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return [self testFunc:obj];
}]];

Solution 4 - Objective C

If you are OS X 10.6/iOS 4.0 or later, you're probably better off with blocks than NSPredicate. See -[NSArray indexesOfObjectsPassingTest:] or write your own category to add a handy -select: or -filter: method (example).

Want somebody else to write that category, test it, etc.? Check out BlocksKit (array docs). And there are many more examples to be found by, say, searching for e.g. "nsarray block category select".

Solution 5 - Objective C

Assuming that your objects are all of a similar type you could add a method as a category of their base class that calls the function you're using for your criteria. Then create an NSPredicate object that refers to that method.

In some category define your method that uses your function

@implementation BaseClass (SomeCategory)
- (BOOL)myMethod {
    return someComparisonFunction(self, whatever);
}
@end

Then wherever you'll be filtering:

- (NSArray *)myFilteredObjects {
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"myMethod = TRUE"];
    return [myArray filteredArrayUsingPredicate:pred];
}

Of course, if your function only compares against properties reachable from within your class it may just be easier to convert the function's conditions to a predicate string.

Solution 6 - Objective C

NSPredicate is nextstep's way of constructing condition to filter a collection (NSArray, NSSet, NSDictionary).

For example consider two arrays arr and filteredarr:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

filteredarr = [NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];

the filteredarr will surely have the items that contains the character c alone.

to make it easy to remember those who little sql background it is

*--select * from tbl where column1 like '%a%'--*

1)select * from tbl --> collection

2)column1 like '%a%' --> NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3)select * from tbl where column1 like '%a%' -->

[NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];

I hope this helps

Solution 7 - Objective C

Checkout this library

https://github.com/BadChoice/Collection

It comes with lots of easy array functions to never write a loop again

So you can just do:

NSArray* youngHeroes = [self.heroes filter:^BOOL(Hero *object) {
    return object.age.intValue < 20;
}];

or

NSArray* oldHeroes = [self.heroes reject:^BOOL(Hero *object) {
    return object.age.intValue < 20;
}];

Solution 8 - Objective C

The Best and easy Way is to create this method And Pass Array And Value:

- (NSArray *) filter:(NSArray *)array where:(NSString *)key is:(id)value{
    NSMutableArray *temArr=[[NSMutableArray alloc] init];
    for(NSDictionary *dic in self)
        if([dic[key] isEqual:value])
            [temArr addObject:dic];
    return temArr;
}

Solution 9 - Objective C

Another category method you could use:

- (NSArray *) filteredArrayUsingBlock:(BOOL (^)(id obj))block {
    NSIndexSet *const filteredIndexes = [self indexesOfObjectsPassingTest:^BOOL (id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
                                       return block(obj);
                                   }];

    return [self objectsAtIndexes:filteredIndexes];
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionlajosView Question on Stackoverflow
Solution 1 - Objective ClajosView Answer on Stackoverflow
Solution 2 - Objective CStuartView Answer on Stackoverflow
Solution 3 - Objective CpckillView Answer on Stackoverflow
Solution 4 - Objective CClay BridgesView Answer on Stackoverflow
Solution 5 - Objective CAshley ClarkView Answer on Stackoverflow
Solution 6 - Objective CDurai Amuthan.HView Answer on Stackoverflow
Solution 7 - Objective CJordi PuigdellívolView Answer on Stackoverflow
Solution 8 - Objective CjalmatariView Answer on Stackoverflow
Solution 9 - Objective CDan RosenstarkView Answer on Stackoverflow