filtragem NSArray em uma nova NSArray em Objective-C
-
02-07-2019 - |
Pergunta
Eu tenho um NSArray
e eu gostaria de criar um novo NSArray
com objetos a partir da matriz original que atender a certos critérios. Os critérios é decidido por uma função que retorna um BOOL
.
Eu posso criar um NSMutableArray
, percorrer a matriz de origem e copiar os objetos que a função de filtro aceita e, em seguida, criar uma versão imutável dele.
Existe uma maneira melhor?
Solução
NSArray
e NSMutableArray
fornecer métodos para conteúdo da matriz do filtro. NSArray
fornece filteredArrayUsingPredicate: que retorna uma nova matriz contendo objetos no receptor que coincide com o predicado especificado. NSMutableArray
acrescenta filterUsingPredicate: que avalia o conteúdo do receptor contra o predicado e folhas especificado somente objetos que jogo. Estes métodos são ilustrados no exemplo seguinte.
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" }
Outras dicas
Há um monte de maneiras de fazer isso, mas, de longe, o mais puro é certamente usando [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 é quase tão concisa quanto ele ganha.
Swift:
Para aqueles que trabalham com NSArray
s na Swift, você pode preferir este mais versão concisa:
nsArray = nsArray.filter { $0.shouldIKeepYou() }
filter
é apenas um método em Array
(NSArray
é implicitamente ponte para Array
de Swift). Leva um argumento: um fechamento que leva um objeto na matriz e retorna um Bool
. No seu encerramento, basta retornar true
por quaisquer objetos que você quer na matriz filtrada.
Se você for OS X 10.6 / iOS 4.0 ou posterior, você é provavelmente melhor fora com blocos que NSPredicate. Consulte -[NSArray indexesOfObjectsPassingTest:]
ou escrever sua própria categoria para adicionar um método -select:
ou -filter:
útil ( exemplo ).
Quer alguém para escrever nessa categoria, testá-lo, etc.? Confira BlocksKit ( docs matriz ). E há muitos mais exemplos que podem ser encontrados por, digamos, à procura de exemplo "categoria bloco NSArray seleccionar ".
Com base em uma resposta por Clay Bridges, aqui está um exemplo de filtragem usando blocos (mudança yourArray
ao seu nome de variável de matriz e testFunc
para o nome da sua função de teste):
yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [self testFunc:obj];
}]];
Assumindo que os objetos são todos de um tipo semelhante, você poderia adicionar um método como uma categoria de sua classe base que chama a função que você está usando para seus critérios. Em seguida, criar um objecto NSPredicate que se refere a esse método.
Em alguns categoria definir o seu método que utiliza a função
@implementation BaseClass (SomeCategory)
- (BOOL)myMethod {
return someComparisonFunction(self, whatever);
}
@end
Então onde quer que você vai ser a filtragem:
- (NSArray *)myFilteredObjects {
NSPredicate *pred = [NSPredicate predicateWithFormat:@"myMethod = TRUE"];
return [myArray filteredArrayUsingPredicate:pred];
}
É claro que, se a sua função compara apenas contra propriedades acessível a partir de dentro de sua classe só ele pode ser mais fácil para converter as condições da função para uma cadeia de predicado.
NSPredicate
é a maneira da nextstep de construir condições para filtrar uma coleção (NSArray
, NSSet
, NSDictionary
).
Por exemplo, considere duas matrizes arr
e filteredarr
:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];
filteredarr = [NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];
o filteredarr certamente terá os itens que contém o caractere c sozinho.
para torná-lo fácil de lembrar aqueles que pouco de fundo sql é
*--select * from tbl where column1 like '%a%'--*
1) SELECT * FROM tbl -> coleção
2) column1 como '% a%' -> NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];
3) SELECT * FROM tbl onde column1 como '% a%' ->
[NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];
Espero que isso ajude
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
Checkout esta biblioteca
https://github.com/BadChoice/Collection
Ele vem com muitas funções de matriz fáceis para nunca escrever um loop novamente
Assim, 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;
}];
A melhor maneira e fácil é criar este método e passar array e Valor:
- (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;
}