Solution: 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 indexesOfObjectsWithOptions:passingTest:
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 NSArray
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:
-(MyObject*)findObjectWithUUID:(NSString*)searchUUID{ for (MyObject* obj in _myArray){ if([obj.uuid isEqualToString: searchUUID]) return obj; } }
Finding an array of objects:
-(NSArray*)findObjectsWithFlag:(BOOL)f{ 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 indexOfObject:
- (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 --
Checking Class
and implementing hash
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]; }
The main difference is that NSArray is for an ordered collection and NSSet is for an unordered collection. There are several articles out there that talk about the difference in speed between the two, like this one. If you're iterating through an unordered collection, NSSet is great.
The primary difference between NSArray and NSMutableArray is that a mutable array can be changed/modified after it has been allocated and initialized, whereas an immutable array, NSArray , cannot.
arrays can't contain nil.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With