Domanda

Ho un NSArray e vorrei creare un nuovo NSArray con oggetti dell'array originale che soddisfano determinati criteri. I criteri sono decisi da una funzione che restituisce un BOOL .

Posso creare un NSMutableArray , scorrere attraverso l'array di origine e copiare gli oggetti accettati dalla funzione filtro e quindi crearne una versione immutabile.

C'è un modo migliore?

È stato utile?

Soluzione

NSArray e NSMutableArray forniscono metodi per filtrare i contenuti dell'array. NSArray fornisce filteredArrayUsingPredicate: che restituisce un nuovo array contenente oggetti nel ricevitore che corrispondono al predicato specificato. NSMutableArray aggiunge filterUsingPredicate: che valuta il contenuto del destinatario rispetto al predicato specificato e lascia solo gli oggetti corrispondenti. Questi metodi sono illustrati nell'esempio seguente.

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

Altri suggerimenti

Ci sono molti modi per farlo, ma di gran lunga il più pulito sta sicuramente 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.
}]];

Penso che sia il più conciso possibile.


Swift:

Per coloro che lavorano con NSArray in Swift, potresti preferire questa versione ancora più concisa:

nsArray = nsArray.filter { <*>.shouldIKeepYou() }

filter è solo un metodo su Array ( NSArray è implicitamente collegato al Array di Swift). Prende un argomento: una chiusura che accetta un oggetto nell'array e restituisce un Bool . Nella tua chiusura, ti basta restituire true per tutti gli oggetti che desideri nella matrice filtrata.

Se sei OS X 10.6 / iOS 4.0 o successivo, probabilmente stai meglio con i blocchi di NSPredicate. Vedi - [NSArray indexesOfObjectsPassingTest :] o scrivi la tua categoria per aggiungere un pratico -select: o -filter: metodo ( esempio ).

Vuoi che qualcun altro scriva quella categoria, la collauda, ??ecc.? Dai un'occhiata a BlocksKit ( array docs ). E ci sono molti altri esempi che possono essere trovati, ad esempio, cercando ad es. " categoria blocco nsarray seleziona ".

Sulla base di una risposta di Clay Bridges, ecco un esempio di filtro che utilizza i blocchi (cambia yourArray nel nome della tua variabile di array e testFunc nel nome della tua funzione di test ):

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

Supponendo che i tuoi oggetti siano tutti di un tipo simile potresti aggiungere un metodo come categoria della loro classe base che chiama la funzione che stai usando per i tuoi criteri. Quindi crea un oggetto NSPredicate che faccia riferimento a quel metodo.

In alcune categorie definisci il tuo metodo che utilizza la tua funzione

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

Quindi, ovunque tu filtrerai:

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

Naturalmente, se la tua funzione si confronta solo con proprietà raggiungibili all'interno della tua classe, potrebbe essere più semplice convertire le condizioni della funzione in una stringa predicata.

NSPredicate è il modo di nextstep di costruire condizioni per filtrare una raccolta ( NSArray , NSSet , NSDictionary ).

Ad esempio, considera due array arr e filteredarr :

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

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

il filteredarr avrà sicuramente gli oggetti che contengono solo il carattere c.

per semplificare il ricordo di chi ha un background sql basso

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

1) seleziona * da tbl - > raccolta

2) colonna1 come '% a%' - > NSPredicate * predicate = [NSPredicate predicateWithFormat: @ " SELF contiene [c]% @ ", @ " c "];

3) seleziona * da tbl dove colonna1 come '% a%' - >

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

Spero che questo aiuti

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

Puoi scaricare questa categoria qui

Dai un'occhiata a questa libreria

https://github.com/BadChoice/Collection

Viene fornito con molte semplici funzioni di array per non scrivere mai più un ciclo

Quindi puoi semplicemente fare:

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

o

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

Il modo migliore e semplice è creare questo metodo e passare array e valore:

- (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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top