Uptown Apps Uptown Apps - 1 year ago 167
iOS Question

NSArray find object or objects - best practices

I have marked @BlackRider's answer as correct as it is the most versatile especially for complex comparisons however there are other very good answers and comments. I would encourage anyone with the same or similar question to review them and evaluate the best course of action for your specific situation.

In my situation, I am actually not using BlackRider's solution in my implementation. I have elected to use my own solution (see Edit #2 below) with help from @JoshCaswell's comments as well as @voromax's suggestion of

due to the fact that my comparisons are very simple in this situation.

Thanks to everyone who answered and provided insight.

I am looking for an efficient way to retrieve an object from an
based on a property of that object (a unique identifier, in this case). In C#.NET using Linq I would do something like

MyObject obj = myList.Single(o => o.uuid == myUUID);

I am also wondering if there is an efficient way to get an array of objects matching a non-unique property. Again, with Linq it would look like

List<MyObject> objs = myList.Where(o => o.flag == true).ToList();

Of course I can write loops to do this but they would not be reusable and I'm suspicious of their performance.

Finding an object with a unique ID:

for (MyObject* obj in _myArray){
if([obj.uuid isEqualToString: searchUUID])
return obj;

Finding an array of objects:

NSMutableArray* arr = [NSMutableArray array];
for (MyObject* obj in _myArray){
if(obj.flag == f)
[arr addObject:obj];
return arr;

-- EDIT --

Luckily in the first situation the object I am looking for has a unique identifier and I know there will only be one. I came up with a solution to implement isEqual on my object which will be invoked by

- (BOOL)isEqual:(id)object{
return [self.uuid isEqualToString: ((MyObject*)object).uuid];

And then create a "fake" lookup object and use that to find the real one

MyObject *lookupObject = [[MyObject alloc] init];
lookupObject.uuid = searchUUID;
MyObject *actualObject =
[_myArray objectAtIndex:[_myArray indexOfObject:lookupObject]];

This is essentially the same as the for-in loop I posted above, but might be more readable & be more reusable. Of course, this only works for finding one unique object and does not address the second half of my question.

-- EDIT 2 --

and implementing
as recommended in comments.

- (BOOL)isEqual:(id)object{
return [object isKindOfClass:[MyObject class]] &&
[self.uuid isEqualToString: ((MyObject*)object).uuid];

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

Answer Source

You can use [NSPredicate], which gives you a query-like syntax for search. Check out this page for the predicate syntax description. Here's a simple example:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"propertyName == %@", @"value"];
NSArray *filteredArray = [myArray filteredArrayUsingPredicate:predicate];

As to performance, I think your solution is OK since any search in an array needs to iterate through all the elements anyway, and then, for each object, compare the value of a field against the value you search for. You can optimize repeat searches within the same data, e.g. by creating and populating a dictionary that maps values of some field to the matching objects (or collections of objects, if the mapping is one to many).