Utiliser un prédicat avec un tableau d'argument
Bonjour,
je suis navré si cette question peut paraitre bête mais je ne trouves pas de réponse dans la doc ni sur le net.
Je cherche à savoir si un Set (mais ça peut être un array ça m'est égal) contient un string dont la première lettre commence par celle d'autres objets présent dans un autre tableau. J'arrive à comparer si je fourni l'objet en dur mais pas en passant par un tableau.
Voici le code que j'ai :
NSMutableSet *aSet = [[NSMutableSet alloc]initWithObjects:@J, @T, nil];
NSString *nomDeFleurRecherchee = @Rose_De_Sable;
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:@nomDeLaFleurRepresentee beginswith[c] %@ || nomDeLaFleurRepresentee beginswith[c] %@", [[nomDeFleurRecherchee componentsSeparatedByString:@_]objectAtIndex:0], aSet]; // [aSet allKeys] ne fonctionne pas non plus.
verifSet = (NSMutableSet*)[nSet filteredSetUsingPredicate:sPredicate]; // ca plante à cet endroit si on doit passer après le || en me disant que ce ne sont pas des strings comme attendu mais bien une classe collection.
En fait j'aimerai que pour chaque objet qui est comparé dans nSet, il le compare à tous les objets présent dans aSet, voir même pouvoir effectuer une action sur tous les objets présent dans aSet, comme par exemple récupérer une partie du nom avec componentsSeparatedByString dans le cas où aSet contiendrait des noms entiers comme @Tulipe_Rouge. Est ce que tout cela est possible ?
j'ai trouvé la méthode objectPassingTest, mais j'avoue ne pas bien la comprendre et n'arrive pas à savoir si elle peut m'être utile, car j'ai l'impression qu'elle s'applique à l'objet qui passe le test dans nSet et non dans aSet.
Quelqu'un pourrait-il me dire comment arriver à réaliser quelque chose qui ressemblerai à ça en pseudo code :
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:@nomDeLaFleurRepresentee beginswith[c] %@ /*la première lettre ou le premier mot de chaque objet présents */ IN %@", ???, aSet];
Ca m'enlèverai vraiment une épine du pied.
Merci beaucoup.
Réponses
Mais d'après ce que j'ai compris de ton besoin, c'est peut-être mieux de passer par un bloc qu'un NSPredicate du coup, d'autant que si tu arrives à écrire un NSPredicate qui fait ça (alors que tu veux à ce que je comprend limite pouvoir faire des actions comme couper le nom en morceaux pour la comparaison, etc, ce que les NSpredicate ne peuvent pas vraiment faire de toute façon), il risque de toute façon d'être assez costaud et illisible.
Il est préférable d'avoir un code lisible qu'un code avec un predicate alambiqué et impossible à relire.
Donc moi je ferais un truc du style : Avec ce code, dans ton NSSet 'found' tu as tous les objet de nSet qui commencent par une des lettres présentes dans aSet. Je suis pas sûr que c'est vraiment ça que tu voulais faire car j'ai pas tout compris de tes explications mais bon si c'est pas ça je suis sûr que tu sauras adapter.
Merci beaucoup pour cette réponse. J'avais peur en effet de ne pas être assez précis mais ta réponse à été assez claire et en effet mieux vaut ne pas utiliser de prédicats s'ils rendent le code illisible.
En fait j'ai une première collection qui contient des customs objets, mon seuls moyens pour retrouver ces objets et les identifier est de passer par leur attribut qui est un nom souvent composé,( par exemple roseRouge possède l'attribut @Rose_Rouge comme nom).
Dans une deuxième collection je n'ai que des strings qui contiennent la première partie du nom, (par exemple @Rose_Jaune se transforme en @Rose).
J'aimerais donc trouver dans ma première collection tous les objets dont l'attribut nom correspond aux noms présent dans ma deuxième collection. Ainsi si j'ai roseJaune dans l'un et roseRouge dans l'autre, il doit détecter que la rose est commune au deux collection même si ce n'est pas le même objet et quelque soit la couleur ou le type.
Grace à ton code et tes explications j'ai pu comprendre la méthode objectsPassingTest et trouver comment régler mon problème, j'ai donc fais ça :
Un grand merci pour ces explications ! ! !
Mais ne faudrait-il pas mieux que tu repenses ton modèle pour avoir deux attributs 'type' et 'couleur' sur ton entité 'Fleur' ?
Quitte à ce que tu fournisses un constructeur qui, à partir du nom composé (que tu as peut-être parce que tu récupères tes données d'un WebService ou d'un fichier CSV ou autre qui ne te liste les fleurs que sous la forme "Rose_Rouge" et "Rose_Jaune"), te sépare ces 2 sous-chaà®nes (componentsSeparatedByString:@_) et te crée l'entité Fleur avec son type et sa couleur correctement ?
Quand on met en place un modèle de données comme cela, il n'est pas toujours bon de coller un-pour-un avec la source de données qui va servir à remplir la base (WebService, fichier CSV ou autre).
- Le WebService peut décider un jour de changer de format (et qui sait, ne plus mettre de "_" mais des espaces ?) et il ne faudrait pas que tu aies à changer tout ton code qui suppose que les fleurs sont décrites avec leur seul nom et ces "_" dans le cas où ça change
- Pour manipuler tes données ensuite, naviguer dans ton modèle de données, manipuler tes fleurs, faire des NSPredicate dessus justement, etc, ça sera bien plus simple si tu manipules des objets qui ont une propriété 'type' et une propriété 'couleur' séparés, plutôt que d'avoir un nom un peu bâtard composé d'un mix des deux.
Ainsi si tes entités Fleur avaient eu dès le départ 2 attributs bien distincts pour type et couleur, tu aurais eu beaucoup moins de mal à faire ton NSPredicate. Rien ne t'empêche ensuite d'avoir une méthode 'fullname' qui reconstruit une sorte de nom complet de ta fleur composé du type + la couleur, et tu pourrais alors écrire un [NSPredicate predicateWithFormat:@SELF.type == %@ OR SELF.fullname IN %@", nomDeFleurRecherchee, verifSet] par exemple.
Après la solution que tu m'as donné plus haut fonctionne très bien, mais peut-être est ce un peu moins élégant que d'ajouter un nouvel attribut ? Je pense ne pas y avoir pensé car ça me fait en quelque sorte dupliquer une information, si je peux obtenir A à partir de B, je ne voyais pas l'intérêt d'enregistrer A en tant qu'attribut, j'essaie de faire attention à la mémoire et je me disais qu'il valait mieux retrouver tout ça au moment voulu. Mais au niveau performance un prédicat est peut-être plus efficient que de créer de nouvelles strings à chaque fois pour pouvoir les comparer ? Je suppose en tout cas.
Je vais donc m'en tenir à ta solution de créer un attribut type qui sera la première partie du nom complet. Pour moi pas de soucis que le nom ou les "_" changent en cours de route, je suis seul sur le projet, mais merci de pointer cette éventualité.
Mais ne peux-tu pas te passer entièrement du nom complet ?
Si tu veux chercher toutes les fleurs qui sont exactement des "Rose_Rouge" tu peux tout à fait faire un prédicat qui filtre toutes les fleurs dont le type est "Rose" et la couleur est "Rouge" ;-)
Pour moi, il faut te débarrasser définitivement de ce nom complet bâtard.
Après si tu tiens vraiment à le garder, une autre possibilité c'est de faire une méthode qui s'appelle nom_complet et qui retourne la concaténation de type + "__" + couleur, et tu utilises cette propriété calculée nom_complet dans ton prédicat. Comme ça, tu ne stockes pas les données en double, le nom complet n'étant pas stocké mais juste recalculé à la demande.
(NB : Cette solution ne marchera pas avec CoreData car le prédicat est converti une clause SQL quand tu l'utilises avec une NSFetchRequest et les clauses SQL ne peuvent pas utiliser une propriété calculée via du code ObjC, mais si ton modèle n'est pas du CoreData mais des NSSet et NSArray sur lesquels tu fais des filterUsingPredicate alors pas de soucis)
Je pourrais attribuer directement une Fleur aux objets Tige mais ça me semble être trop lourd pour les simple besoins que j'ai. Une tige n'a pas besoin de connaà®tre les attributs de la fleur qu'elle représente,mais simplement son nom pour pouvoir construire plus tard le nom de l'image correspondante. De plus une Fleur ne possède pour le moment qu'un attribut nom, comme je retrouve la variété à partir du nom elle ne connaà®t pas sa variété ni sa couleur, même si ça peut paraà®tre bizarre je l'avoue.
Je n'utilise pas coreData donc cette solution devrait fonctionner. Juste ce que j'ai du mal expliquer c'est que je possède déjà le nom complet, ce qui me manque c'est la variété qui est composée du premier objets de nomComplet componentsSepareted.., donc je peux en effet utiliser une méthode qui calcule la variété à partir du nom, avant de passer ça en argument dans le prédicat. C'est déjà un peu ce que tu me proposais à la fin de ton deuxième message il me semble.