filtrare NSArray in un nuovo NSArray in goal-c
-
02-07-2019 - |
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?
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 ??p>
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
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;
}