фильтрация NSArray в новый NSArray в target-c - PullRequest
115 голосов
/ 21 сентября 2008

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

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

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

Ответы [ 9 ]

134 голосов
/ 21 сентября 2008

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" }
98 голосов
/ 22 октября 2013

Есть множество способов сделать это, но, безусловно, самый лучший, безусловно, использует [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 s в Swift, вы можете предпочесть эту еще более краткую версию:

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

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

49 голосов
/ 04 октября 2010

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

Хотите, чтобы кто-нибудь еще написал эту категорию, протестировал ее и т. Д.? Извлечение BlocksKit ( документы по массиву ). И есть много других примеров, которые можно найти, скажем, в поиске, например. «Выбор категории блоков nsarray» .

44 голосов
/ 08 ноября 2012

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

yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return [self testFunc:obj];
}]];
16 голосов
/ 21 ноября 2008

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

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

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

Тогда где вы будете фильтровать:

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

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

3 голосов
/ 04 апреля 2013

NSPredicate - это способ построения условия nextstep для фильтрации коллекции (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) выбрать * из таблицы -> коллекция

2) столбец 1, такой как «% a%» -> NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3) выберите * из таблицы, где столбец 1, как "% a%" ->

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

Надеюсь, это поможет

2 голосов
/ 16 сентября 2014

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

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

0 голосов
/ 20 ноября 2018

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

- (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;
}
0 голосов
/ 29 августа 2016

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

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;
}];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...