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

Фильтрация NSArray в новый NSArray в Objective-C

У меня есть NSArray, и я хотел бы создать новый NSArray с объектами из исходного массива, которые соответствуют определенным критериям. Критерии определяются функцией, которая возвращает BOOL.

Я могу создать NSMutableArray, перебрать исходный массив и скопировать объекты, которые принимает функция фильтра, а затем создать его неизменную версию.

Есть ли способ лучше?

4b9b3361

Ответ 1

NSArray и NSMutableArray предоставляют методы для фильтрации содержимого массива. NSArray предоставляет FilterArrayUsingPredicate:, который возвращает новый массив, содержащий объекты в приемнике, которые соответствуют указанному предикату. NSMutableArray добавляет filterUsingPredicate:, который сравнивает содержимое получателей с указанным предикатом и оставляет только совпадающие объекты. Эти методы проиллюстрированы в следующем примере.

NSMutableArray *array =
    [NSMutableArray arrayWithObjects:@"Bill", @"Ben", @"Chris", @"Melissa", nil];

NSPredicate *bPredicate =
    [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
NSArray *beginWithB =
    [array filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { @"Bill", @"Ben" }.

NSPredicate *sPredicate =
    [NSPredicate predicateWithFormat:@"SELF contains[c] 's'"];
[array filteredArrayUsingPredicate:sPredicate];
// array now contains { @"Chris", @"Melissa" }

Ответ 2

Существует множество способов сделать это, но, безусловно, опрятный, безусловно, использует [NSPredicate predicateWithBlock:]:

NSArray *filteredArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
    return [object shouldIKeepYou];  // Return YES for each object you want in filteredArray.
}]];

Я думаю, что это будет настолько кратким, насколько это возможно.


Swift:

Для тех, кто работает с NSArray в Swift, вы можете предпочесть еще более сжатую версию:

nsArray = nsArray.filter { $0.shouldIKeepYou() }

filter - это всего лишь метод на Array (NSArray неявно соединен с Swifts Array). Он принимает один аргумент: замыкание, которое принимает один объект в массиве и возвращает a Bool. В своем закрытии просто верните true для любых объектов, которые вы хотите в отфильтрованном массиве.

Ответ 3

Если вы OS X 10.6/iOS 4.0 или новее, вам, вероятно, лучше с блоками, чем NSPredicate. См. -[NSArray indexesOfObjectsPassingTest:] или напишите свою собственную категорию, чтобы добавить удобный метод -select: или -filter: (пример).

Хотите кого-нибудь еще написать эту категорию, проверить ее и т.д.? Проверьте BlocksKit (массивы docs). И есть many больше примеров, которые можно найти, скажем, для поиска, например. "nsarray category category select" .

Ответ 4

На основе ответа Clay Bridges приведен пример фильтрации с использованием блоков (измените yourArray на имя переменной массива и testFunc на имя вашей тестовой функции):

yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return [self testFunc:obj];
}]];

Ответ 5

Предполагая, что ваши объекты имеют одинаковый тип, вы можете добавить метод как категорию своего базового класса, который вызывает функцию, которую вы используете для своих критериев. Затем создайте объект NSPredicate, который ссылается на этот метод.

В какой-то категории определите свой метод, который использует вашу функцию

@implementation BaseClass (SomeCategory)
- (BOOL)myMethod {
    return someComparisonFunction(self, whatever);
}
@end

Затем везде, где вы будете фильтровать:

- (NSArray *)myFilteredObjects {
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"myMethod = TRUE"];
    return [myArray filteredArrayUsingPredicate:pred];
}

Конечно, если ваша функция сравнивается только с свойствами, доступными внутри вашего класса, проще всего преобразовать условия функции в предикатную строку.

Ответ 6

NSPredicate - следующий способ построения условия для фильтрации коллекции (NSArray, NSSet, NSDictionary).

Например, рассмотрим два массива arr и filteredarr:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

filteredarr = [NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];

у фильтратар обязательно будут элементы, содержащие только символ c.

чтобы было легко запомнить тех, у кого мало sql-фона это

*--select * from tbl where column1 like '%a%'--*

1) выберите * из tbl → коллекция

2) column1, как '% a%'NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3) выберите * из tbl, где column1, например '% a%'

[NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];

Я надеюсь, что это поможет

Ответ 7

NSArray + X.h

@interface NSArray (X)
/**
 *  @return new NSArray with objects, that passing test block
 */
- (NSArray *)filteredArrayPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate;
@end

NSArray + X.m

@implementation NSArray (X)

- (NSArray *)filteredArrayPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
{
    return [self objectsAtIndexes:[self indexesOfObjectsPassingTest:predicate]];
}

@end

Вы можете скачать эту категорию здесь

Ответ 8

Оформить заказ этой библиотеки

https://github.com/BadChoice/Collection

Он поставляется с множеством простых функций массива, чтобы никогда не писать цикл снова

Итак, вы можете просто сделать:

NSArray* youngHeroes = [self.heroes filter:^BOOL(Hero *object) {
    return object.age.intValue < 20;
}];

или

NSArray* oldHeroes = [self.heroes reject:^BOOL(Hero *object) {
    return object.age.intValue < 20;
}];

Ответ 9

Лучший и простой способ - создать этот метод и передать массив и значение:

- (NSArray *) filter:(NSArray *)array where:(NSString *)key is:(id)value{
    NSMutableArray *temArr=[[NSMutableArray alloc] init];
    for(NSDictionary *dic in self)
        if([dic[key] isEqual:value])
            [temArr addObject:dic];
    return temArr;
}