128 votes

filtrer un NSArray en un nouveau NSArray en Objective-C

J'ai un NSArray et je voudrais créer un nouveau NSArray avec des objets du tableau d'origine qui répondent à certains critères. Les critères sont décidés par une fonction qui renvoie un fichier BOOL .

Je peux créer un NSMutableArray Dans ce cas, il faut itérer dans le tableau source et copier les objets que la fonction de filtrage accepte, puis créer une version immuable de ce tableau.

Y a-t-il un meilleur moyen ?

145voto

lajos Points 13791

NSArray y NSMutableArray fournissent des méthodes pour filtrer le contenu des tableaux. NSArray fournit FilteredArrayUsingPredicate : qui renvoie un nouveau tableau contenant les objets du récepteur qui correspondent au prédicat spécifié. NSMutableArray ajoute filterUsingPredicate : qui évalue le contenu du récepteur par rapport au prédicat spécifié et ne laisse que les objets qui correspondent. 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" }

84 votes

J'ai écouté le podcast du Grand Schtroumpf et le Grand Schtroumpf a dit que les réponses devraient être publiées sur StackOverflow pour que la communauté puisse les noter et les améliorer.

10 votes

@mmalc - Peut-être plus approprié, mais certainement plus pratique de le visualiser ici même.

5 votes

NSPredicate est mort, vive les blocs ! cf. ma réponse ci-dessous.

125voto

Stuart Points 8578

Il existe de nombreuses façons de le faire, mais la plus efficace est sans doute 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 aussi concis que possible.


Swift :

Pour ceux qui travaillent avec NSArray en Swift, vous préférerez peut-être cette encore plus version concise :

let filteredArray = array.filter { $0.shouldIKeepYou() }

filter est juste une méthode sur Array ( NSArray est implicitement relié à celui de Swift. Array ). Elle prend un argument : une fermeture qui prend un objet dans le tableau et retourne un objet de type Bool . Dans votre fermeture, il suffit de retourner true pour tous les objets que vous voulez dans le tableau filtré.

1 votes

Quel est le rôle de NSDictionary *bindings ici ?

0 votes

@Kaitain bindings est requis par le programme NSPredicate predicateWithBlock: API.

0 votes

@Kaitain The bindings peut contenir des liaisons de variables pour les modèles.

50voto

pckill Points 1463

Basé sur une réponse de Clay Bridges, voici un exemple de filtrage en utilisant des blocs (changement yourArray à votre nom de variable de tableau et testFunc au nom de votre fonction de test) :

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

1 votes

Enfin une réponse qui non seulement mentionne le filtrage par blocs, mais donne aussi un bon exemple de la façon de le faire. Merci.

0 votes

J'aime cette réponse, même si filteredArrayUsingPredicate est plus légère, le fait que vous n'utilisiez pas de prédicats obscurcit un peu l'objectif.

0 votes

C'est la façon la plus moderne de procéder. Personnellement, je regrette l'ancien raccourci "objectsPassingTest" qui a disparu de l'API à un moment donné. Néanmoins, cette méthode est rapide et efficace. J'aime bien NSPredicate, mais pour d'autres choses, là où le marteau plus lourd est nécessaire.

46voto

Clay Bridges Points 3470

Si vous êtes sous OS X 10.6/iOS 4.0 ou ultérieur, il est probablement préférable d'utiliser des blocs plutôt que NSPredicate. Voir -[NSArray indexesOfObjectsPassingTest:] ou écrivez votre propre catégorie pour ajouter une fonction pratique -select: o -filter: méthode ( exemple ).

Vous voulez que quelqu'un d'autre écrive cette catégorie, la teste, etc. ? Consultez BlocksKit ( documentation sur les tableaux ). Et il existe beaucoup de plus d'exemples à trouver en recherchant, par exemple, par ex. "nsarray block category select" .

7 votes

Pourriez-vous compléter votre réponse par un exemple ? Les sites de blogs ont tendance à mourir au moment où vous en avez le plus besoin.

0 votes

Ajouté plus de suggestions en cas de linkrot.

8 votes

La protection contre la pourriture des liens consiste à extraire le code et les autres éléments pertinents des articles liés, et non à ajouter des liens supplémentaires. Gardez les liens, mais ajoutez des exemples de code.

17voto

Ashley Clark Points 6806

En supposant que vos objets soient tous d'un type similaire, vous pourriez 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 une certaine catégorie, définissez votre méthode qui utilise votre fonction

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

Ensuite, où que vous soyez, vous filtrerez :

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

Bien sûr, si votre fonction ne compare que des propriétés accessibles à partir de votre classe, il peut être plus facile de convertir les conditions de la fonction en une chaîne de prédicats.

0 votes

J'ai aimé cette réponse, car elle est gracieuse et courte, et évite d'avoir à réapprendre comment formaliser un NSPredicate pour la chose la plus basique - accéder aux propriétés et aux méthodes booléennes. Je pense même que le wrapper n'était pas nécessaire. Un simple [myArray filteredArrayUsingPredicate :[NSPredicate predicateWithFormat:@"myMethod = TRUE"]] ; suffirait. Merci ! (J'aime aussi les autres solutions, mais celle-ci est agréable).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X