Подтвердить что ты не робот

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];
}
4b9b3361

Ответ 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 секунд или более.