it-swarm.com.ru

фильтрация NSArray в новый NSArray в target-c

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

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

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

110
lajos

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" }
132
lajos

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

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

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

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

91
Stuart

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

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

49
Clay Bridges

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

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

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

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

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

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

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

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

16
Ashley Clark

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) column1 как "% a%" -> NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

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

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

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

3
Durai Amuthan.H

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

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

2
skywinder

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

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;
}];
0
Jordi Puigdellívol

Лучший и простой способ - создать этот метод And Pass Array And Value:

- (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
jalmatari