web-dev-qa-db-fra.com

filtrage de NSArray dans un nouveau NSArray dans Objective-C

J'ai une NSArray et j'aimerais créer une nouvelle NSArray avec des objets du tableau d'origine qui répondent à certains critères. Le critère est décidé par une fonction qui renvoie BOOL.

Je peux créer une variable NSMutableArray, parcourir le tableau source et copier sur les objets acceptés par la fonction de filtrage, puis en créer une version immuable. 

Y a-t-il un meilleur moyen?

110
lajos

NSArray et NSMutableArray fournissent des méthodes pour filtrer le contenu des tableaux. NSArray fournit filterArrayUsingPredicate: qui renvoie un nouveau tableau contenant les objets du récepteur qui correspondent au prédicat spécifié. NSMutableArray ajoute filterUsingPsingicate: qui évalue le contenu du destinataire par rapport au prédicat spécifié et ne laisse que les objets correspondants. Ces méthodes sont illustrées dans l'exemple suivant.

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

Il existe de nombreuses façons de le faire, mais de loin le plus simple est sûrement d'utiliser [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.
}]];

Je pense que c'est à peu près aussi concis que possible.


Rapide:

Pour ceux qui travaillent avec NSArrays dans Swift, vous préférerez peut-être cette version concise même plus:

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

filter est simplement une méthode sur Array (NSArray est implicitement pontée vers Array de Swift). Cela prend un argument: une fermeture qui prend un objet dans le tableau et retourne une Bool. Dans votre clôture, retournez simplement true pour tous les objets que vous voulez dans le tableau filtré.

91
Stuart

Si vous utilisez OS X 10.6/iOS 4.0 ou une version ultérieure, vous êtes probablement mieux avec des blocs que NSPredicate. Voir -[NSArray indexesOfObjectsPassingTest:] ou écrivez votre propre catégorie pour ajouter une méthode pratique -select: ou -filter: ( exemple ).

Vous voulez que quelqu'un d'autre écrive cette catégorie, la teste, etc.? Départ BlocksKit ( Docs de tableau ). Et il y a beaucoup d’autres exemples à trouver, par exemple, en recherchant par exemple. "nsarray block category select" .

49
Clay Bridges

D'après une réponse de Clay Bridges, voici un exemple de filtrage à l'aide de blocs (remplacez yourArray par le nom de votre variable de tableau et testFunc par le nom de votre fonction de test):

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

En supposant que vos objets soient tous d'un type similaire, vous pouvez ajouter une méthode en tant que catégorie de leur classe de base qui appelle la fonction que vous utilisez pour vos critères. Créez ensuite un objet NSPredicate qui fait référence à cette méthode.

Dans certaines catégories, définissez votre méthode qui utilise votre fonction

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

Ensuite, partout où vous filtrerez:

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

Bien sûr, si votre fonction ne compare que les propriétés accessibles depuis votre classe, il sera peut-être plus simple de convertir les conditions de la fonction en chaîne de prédicat.

16
Ashley Clark

NSPredicate est la façon dont NeXTSTEP a construit une condition pour filtrer une collection (NSArray, NSSet, NSDictionary).

Par exemple, considérons deux tableaux arr et filteredarr:

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

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

filterarr aura sûrement les éléments contenant le caractère c seul.

pour qu'il soit facile de se souvenir de ceux qui ont peu de fond SQL 

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

1) select * from tbl -> collection

2) colonne1 comme '% a%' -> NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3) select * from tbl où column1 comme '% a%' ->

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

J'espère que ça aide 

3
Durai Amuthan.H

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

Vous pouvez télécharger cette catégorie ici

2
skywinder

Commander cette bibliothèque

https://github.com/BadChoice/Collection

Il contient de nombreuses fonctions de tableau faciles à ne jamais écrire une boucle

Alors vous pouvez juste faire:

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;
}];
0
Jordi Puigdellívol

La méthode la plus simple et la plus simple consiste à créer cette méthode, ainsi que Passe-tableau et valeur

- (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;
}
0
jalmatari