Filtrando NSArray em um novo NSArray em object-c

Eu tenho um NSArray e gostaria de criar um novo NSArray com objects do array original que atendem a determinados critérios. O critério é decidido por uma function que retorna um BOOL .

Eu posso criar um NSMutableArray , percorrer a matriz de origem e copiar sobre os objects que a function de filtro aceita e, em seguida, criar uma versão imutável do mesmo.

Existe uma maneira melhor?

NSArray e NSMutableArray fornecem methods para filtrar o conteúdo da matriz. NSArray fornece NSArray : que retorna um novo array contendo objects no receptor que correspondem ao predicado especificado. NSMutableArray adiciona filterUsingPredicate: que avalia o conteúdo do receptor em relação ao predicado especificado e deixa apenas objects correspondentes. Esses methods são ilustrados no exemplo a seguir.

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

Existem muitas maneiras de fazer isso, mas de longe o mais [NSPredicate predicateWithBlock:] é certamente usar o [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. }]]; 

Eu acho que é tão conciso quanto parece.


Rápido:

Para aqueles que trabalham com o NSArray s no Swift, você pode preferir esta versão ainda mais concisa:

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

filter é apenas um método no Array (o NSArray é implicitamente vinculado ao Array do Swift). Leva um argumento: um fechamento que leva um object na matriz e retorna um Bool . Em seu fechamento, apenas retorne true para qualquer object que você queira na matriz filtrada.

Se você é o OS X 10.6 / iOS 4.0 ou posterior, provavelmente é melhor ter blocos do que o NSPredicate. Veja -[NSArray indexesOfObjectsPassingTest:] ou escreva sua própria categoria para adicionar um prático -select: ou -filter: method ( exemplo ).

Quer que alguém escreva essa categoria, teste, etc.? Confira o BlocksKit ( documentos de matriz ). E há muitos outros exemplos a serem encontrados, por exemplo, procurando por “nsarray block category select” .

Com base em uma resposta de Clay Bridges, aqui está um exemplo de filtragem usando blocos (altere yourArray para seu nome de variável de array e testFunc para o nome de sua function de teste):

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

Supondo que seus objects sejam todos de um tipo semelhante, você poderia adicionar um método como uma categoria de sua class base que chama a function que você está usando para seus critérios. Em seguida, crie um object NSPredicate que se refira a esse método.

Em alguma categoria defina seu método que usa sua function

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

Então, onde quer que você esteja filtrando:

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

É claro que, se a sua function só comparar com propriedades alcançáveis ​​dentro de sua class, pode ser mais fácil converter as condições da function em uma cadeia de predicados.

NSPredicate é o caminho do nextstep de construir condições para filtrar uma coleção ( NSArray , NSSet , NSDictionary ).

Por exemplo, considere dois arrays arr e filteredarr :

 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"]; filteredarr = [NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]]; 

o filtrarr certamente terá os itens que contêm o caractere c sozinho.

para tornar mais fácil lembrar aqueles que pouco fundo sql é

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

1) selecione * da coleção tbl ->

2) column1 como ‘% a%’ -> NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3) selecione * de tbl onde coluna1 como ‘% a%’ ->

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

Eu espero que isso ajude

NSArray + Xh

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

NSArray + Xm

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

Você pode baixar esta categoria aqui

Checkout esta biblioteca

https://github.com/BadChoice/Collection

Ele vem com muitas funções fáceis de array para nunca mais escrever um loop

Então você pode apenas fazer:

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

ou

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