NSArray находит объект или объекты - лучшие практики
Решение:
Я отметил ответ @BlackRider как правильный, поскольку он является наиболее универсальным, особенно для сложных сравнений, однако есть и другие очень хорошие ответы и комментарии. Я призываю кого-либо с тем же или подобным вопросом рассмотреть их и оценить наилучший курс действий для вашей конкретной ситуации.
В моей ситуации я фактически не использую решение BlackRider в своей реализации. Я решил использовать свое решение (см. Редактировать № 2 ниже) с помощью комментариев @JoshCaswell, а также предложение @voromax indexesOfObjectsWithOptions:passingTest:
из-за того, что мои сравнения очень просты в этой ситуации.
Спасибо всем, кто ответил и дал понять.
Я ищу эффективный способ извлечения объекта из NSArray
на основе свойства этого объекта (в этом случае уникальный идентификатор). В С#.NET с использованием Linq я бы сделал что-то вроде
MyObject obj = myList.Single(o => o.uuid == myUUID);
Мне также интересно, есть ли эффективный способ получить массив объектов, соответствующих неистинному свойству. Опять же, с Linq это выглядело бы как
List<MyObject> objs = myList.Where(o => o.flag == true).ToList();
Конечно, я могу писать циклы, чтобы сделать это, но они не будут повторно использоваться, и я подозрюю их производительность.
Поиск объекта с уникальным идентификатором:
-(MyObject*)findObjectWithUUID:(NSString*)searchUUID{
for (MyObject* obj in _myArray){
if([obj.uuid isEqualToString: searchUUID])
return obj;
}
}
Поиск массива объектов:
-(NSArray*)findObjectsWithFlag:(BOOL)f{
NSMutableArray* arr = [NSMutableArray array];
for (MyObject* obj in _myArray){
if(obj.flag == f)
[arr addObject:obj];
}
return arr;
}
- EDIT -
К счастью, в первой ситуации объект, который я ищу, имеет уникальный идентификатор, и я знаю, что его будет только один. Я придумал решение для реализации isEqual для моего объекта, который будет вызываться indexOfObject:
- (BOOL)isEqual:(id)object{
return [self.uuid isEqualToString: ((MyObject*)object).uuid];
}
И затем создайте "поддельный" объект поиска и используйте это, чтобы найти реальный
MyObject *lookupObject = [[MyObject alloc] init];
lookupObject.uuid = searchUUID;
MyObject *actualObject =
[_myArray objectAtIndex:[_myArray indexOfObject:lookupObject]];
Это по существу то же самое, что и для цикла for-in, который я разместил выше, но может быть более читаемым и быть более многоразовым. Конечно, это работает только для поиска одного уникального объекта и не затрагивает вторую половину моего вопроса.
- EDIT 2 -
Проверка Class
и реализация hash
, как рекомендовано в комментариях.
- (BOOL)isEqual:(id)object{
return [object isKindOfClass:[MyObject class]] &&
[self.uuid isEqualToString: ((MyObject*)object).uuid];
}
- (NSUInteger)hash{
return [self.uuid hash];
}
Ответы
Ответ 1
Вы можете использовать [NSPredicate]
, который дает вам запрос-подобный синтаксис для поиска. Ознакомьтесь с этой страницей для описания синтаксиса предиката. Вот простой пример:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"propertyName == %@", @"value"];
NSArray *filteredArray = [myArray filteredArrayUsingPredicate:predicate];
Что касается производительности, я думаю, что ваше решение в порядке, так как любой поиск в массиве должен все равно проходить через все элементы, а затем для каждого объекта сравнивать значение поля со значением, которое вы ищете. Вы можете оптимизировать повторные поиски в пределах одних и тех же данных, например. путем создания и заполнения словаря, который сопоставляет значения некоторого поля подходящим объектам (или наборам объектов, если отображение одного для многих).
Ответ 2
Вы также можете посмотреть на современный синтаксис блока: indexOfObjectWithOptions:passingTest:
или indexesOfObjectsWithOptions:passingTest:
, которые поддерживают concurrency и порядок поиска.
Ответ 3
Я был заинтригован комментарием rmaddys, поэтому я проверил разницу между циклом и предикатом.
Возьмем простой объект с свойством NSString. Я вставлял его в массив 10 000 раз каждый раз с различным значением свойства.
В худшем случае, когда желаемый объект находился в последней позиции массива, подход цикла был в 3,5 раза быстрее, чем NSPredicate (0.39s против 0.11s, arraySize = 10000, 10 итераций, iPad Mini).
Код, который я использовал для ссылки: pastebin
Ответ 4
Я знаю, что это связано с NSArray
, но если мы это сделаем с помощью Swift
и используя быстрый массив, который является struct
, то это будет намного проще.
Swift 2.2/Swift 3.0 Работает отлично в обеих версиях
Предположим, что у нас есть пользовательский класс модели
class User {
var userId = 0
var userName = ""
}
И давайте предположим, что у нас есть массив с именем usersArray
, у которого есть пользовательские объекты класса User
.
И мы хотим получить объект из этого массива с помощью userId = 100
, например: -
let filteredArray = usersArray.filter({$0.userId == 100})
Этот отфильтрованный массив будет содержать все пользовательские объекты с userId
как 100
print(filteredArray[0].userName) //will print the name of the user with userId = 100
Ответ 5
только для тех, кто заинтересован, я нашел самый быстрый способ поиска через NSArray, используя цикл for в фоновом потоке. используя метод [self performSelectorInBackground...].
В NSArray из 10000 пользовательских объектов я тщательно просмотрел все это примерно за 1 секунду. В основной теме это заняло около 10 секунд или более.