问题
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];
}
回答1:
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).
回答2:
You may also look at modern block syntax: indexOfObjectWithOptions:passingTest: or indexesOfObjectsWithOptions:passingTest: which support concurrency and search order.
回答3:
I was intrigued by rmaddys comment so I've checked the difference between looping and predicate.
Let's assume a simple object with NSString property. I've inserted it into array 10 000 times , every time with different property value.
In the worst case scenario when desired object was on the last position of the array, loop approach was 3.5x faster than NSPredicate (0.39s vs 0.11s, arraySize = 10000, 10 iterations, iPad Mini)
Code I used for reference: pastebin
回答4:
just for those who are interested, I've found the fastest way to search through NSArray is by using a for loop on a background thread. using the [self performSelectorInBackground...] method. In an NSArray of 10000 custom objects I searched through the whole thing thoroughly in around 1 second. On the main thread it took around 10 seconds or more.
回答5:
I know its related with NSArray
but if we do it using Swift
and using the swift Array which is a struct
, then that will be lot easier.
Swift 2.2 / Swift 3.0 / Swift 4.x Working fine on all versions
Lets assume we have a custom model class
class User {
var userId = 0
var userName = ""
}
And lets assume we have an array named as usersArray
which has custom objects of User
class.
And we want to fetch an object from this array with userId = 100
for example:-
let filteredArray = usersArray.filter({$0.userId == 100})
This filtered array will contain all the custom objects which have userId
as 100
print(filteredArray[0].userName) //will print the name of the user with userId = 100
来源:https://stackoverflow.com/questions/20024937/nsarray-find-object-or-objects-best-practices