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?

Foi útil?

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 NSArrays 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

Você pode baixar esta categoria aqui

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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top