[Résolu] Supprimer des objets en double dans un NSArray
ObjectiveSwift
Membre
Bonsoir à tous,
Voilà , j'ai un tableau que j'alimente d'objets. Dans le tas, certains sont en double et je cherche un moyen de les supprimer. Je ne peux pas le faire au moment de l'ajout car ce sont des processus asynchrone.
Je pense donc faire ça ensuite, une fois le tableau peuplé.
Il faut donc que je test en comparant les objets et quand ils sont identiques, supprime le double.
Pour ce faire, je ne vois que prendre le premier objet et parcourir le tableau, puis le deuxième et parcourir le tableau et ainsi de suite.
Suis-je à côté de la plaque ? Ou pas ? Et si oui, quel est le moyen le plus simple/rapide sans trop consommer de ressources.
Merci.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Mais se serait sans doute plus simple d'ajouter tes objets dans un NSMutableSet plutôt que dans un NSMutableArray, les objets égaux à un objet déjà dans le Set ne sont pas ajoutés.
Ce que jpimbert a oublié de te signaler enfin c'était implicite mais il faut que tu définisses la méthode :
C'est la méthode appelé par l'objet Set pour définir qu'est-ce qu'un objet égale.
Si tu utilise juste la ligne de @jpimbert il va appeler la méthodes isEqual de NSObject et donc va ne vérifier qu'une chose : si les 2 objets ont la même adresse mémoire (correspondant à mon premier if dans le code du dessus)
Bref mais l'intention de jpimbert était bien là . Après à toi de lire la doc sur NSSet
je t'ai donné un exemple à toi de le mettre à ta sauce
Mais quand on implemente isEqual: il fait toujours implémenter aussi la méthode hash, c'est important pour quand t'es objets sont ensuite mis dans des conteneurs comme NSDictionary ou NSSet car la première phase de la comparaison de 2 objets passe par cette méthode pour accélérer les comparaisons.
Attention à respecter les propriétés d'un bon hash quand tu implémentes la méthode hash du coup :
- deux objets identiques doivent avoir la même valeur de hash
- deux objets qui ont des valeurs de bash différentes sont forcément des objets différents
- par contre deux objets qui ont le même hash sont pas forcément égaux. Ils ont juste une bonne probabilité de l'être ; et dans ce cas isEqual est appelé pour faire le test final
Le plus simple pour démonter une telle méthode est de combiner les hash des propriétés de ton objet, par exemple avec l'opérateur XOR. Par exemple si ton objet est défini par 2 propriétés "firstName" et "lastName" de type NSString, alors tu peux simplement implémenter la méthode hash pour qu'elle return self.firstName.hash ^ self.lastName.hash;. Et du coup implémenter -(BOOL)isEqual:(TonObjet*)other pour qu'il return [self.firstName isEqual:other.firstName] && [self.lastName isEqual:other.lastName];.
Si ce sont des classes de Cocoa comme NSString ou NSDictionnary ou du genre ils ont déjà leur propre implémentation de isEqual: et de hash donc c'est bon.
Une fois que tu auras fait ça, mettre tes objets dans un NS(Mutable)Set va se charger de tout pour toi, en évitant d'ajouter les doublons.
Pour le hash : si ça classe défini un objet avec un identifiant : par exemple identifiant récupéré sur un JSON, XML, Base de données : et qu'il veut mettre à jour son objet (suite à un update) :
Exemple : Voiture id = 42 / nom = Renault Espace / Kilométrage = 100 000 / Vendu = NON
Et que lors d'un refresh il souhaite mettre à jour les données dans 2 jours par exemple à Voiture id = 42 / nom = Renault Espace / Kilométrage = 100 010 / Vendu = OUI
Bah
Ce que tu dis n'est pas faux mais là dans ce cas il a intérêt à dire qu'il sont égaux par identifiant et faire une maj de son objet !
Perso, j'aurais tendance, dans la méthode "isEqual" à comparer un par un chacun des membres de ton objet. Pour les flottants, j'aurais même tendance à utiliser une inégalité (du genre :
pour le cas où elle serait le fruit d'un calcul. J'ai souvent eu des problèmes avec "isEqual".
Sinon, je en vois pas bien comment éviter de comparer les membres un par un, mais si il y a beaucoup d'objets, cela risque d'être lent, il faudrait voir si tu peux optimiser : au moment de la création des tableaux, n'y a t-il pas moyen de savoir si un objet mis dans le premier tableau risque d'être le même qu'un objet mis parallèlement dans le second? Alors, un troisième tableau stockerait des infos sur l'objet (sa place dans le tableau par exemple) qui serait utilisé par une méthode ensuite. (???)
Bah justement ça dépend de la définition que tu donne de objet = un autre.
Si on prend un exemple classique :
Que défini un étudiant = autre : on compare un indentifiant (donc un argument et pas nom prénom car ça ne défini pas l'unicité)
Mais si tu veux définir ce qu'est un objet CoupleEntier : exemple (1,2) = (3,4) tu es obligé de comparer chaque membre
entier1.gauche = entier2.gauche
entier1.droite = entier.droite
etc ...
Merci pour vos réponses. Alors déjà quelques détails supplémentaires :
- Ma classe hérite en effet de NSObject mais tous mes attributs sont des NSString et un NSDictionary.
- Dans ma classe, la seule chose dont j'ai a me préoccuper et le ID qui est un NSString.
Par contre, je ne vois pas trop, dois-je tout ajouter a un NSArray que je "transvase" dans un NSSet ?
Bah l'idéal c'est que tu ai un id et avec -isEqual (ou -hash pas obligatoire mais d'après ce qu'a signalé Ali c'est important car avant de faire un isEqual il va demander au hash de lui retourner quelques chose) tu puisse comparer directement dessus.
Je ne vois pas de problème. L'idéal est que tu utilise NSSet directement dans ce cas et que tu oublis NSArray.
Sauf si après dans ton application tu as besoin que ton NSSet soit une array tu appel la méthode pour convertir
Dans ce cas, la méthode de la classe NSString ("[myString isEqualToString:aString] ") fonctionnera très bien. Combien d'entrées veux-tu traiter?
Si il y en a peu, une double boucle "for" ferait l'affaire, mais au delà d'un certain nombre, cela va poser problème...
Dans un futur proche s'il accroit son nombre de données à traiter il devra retoucher au code pour enlever la double for(). Du coup vaut mieux NSSet
Oui j'ai pas la main sur le nombre d'élément.
La méthode Equal, on est d'accord que je dois l'overrider dans ma classe ?
Am_Me, concernant ton commentaire :
Ce sont forcement les mêmes objets car ils sont extrais du JSON et j'init la même classe pour tout car l'archi du JSON est identique pour chaque objet.
ouep tu l'override (tu peux créer ton propre isEqualAmoi mais ca n'aurai pas trop de sens en objet):
de toute façon du tape sur le clavier - (id)is .. et lui te complete après je t'ai donné un modèle là haut
Ensuite pour le 2nd commentaire oui je suis d'accord c'est pour ça que je ne le fais pas non plus mais juste pour te dire que si tu veux être pointilleux tu peux rajouter cette condition
Par contre, du coup j'ai toujours un NSArray et je dois initialisé un NSSet quand le NSArray est rempli de tous ces objets ? C'est bien ça ?
Bah comme je t'ai dit ton NSArray là tu peux directement le changer en un NSSet.
Mais si tu ne veux pas oui c'est ça dès que ton NSArray est rempli : tu le converti en NSSet en appelant une méthode dont je n'ai plus le nom ça doit être un truc du genre : setWithArray : et lui s'occupera d'insérer les élément un par un et si c'est un doublon il ne l'insérera pas
Oui c'est bien cette méthode (voir message de jpimbert).
Je vais te poser la question autrement, je me suis mal exprimé. J'ai une boucle que tu verras dans mon autre post d'ailleurs. Dans cette boucle, je récupère des données que j'envoie dans un NSArray. Je veux bien le remplacer par un NSSet mais à ce moment, il fera lui-même le test isEqual ???
(c'est un peu compliquer à tester là vu que mon UITableView est vide donc je pose la question)
Je vais faire peut-être mon rabajoie mais la doc est là pour ça NSSet
Alors, comme ça ne fonctionne pas, voici mon code de l'override de isEqual :
Comme ça ne fonctionne pas, je me suis dis que j'avais peut-être fais une bêtise en changeant 2-3 trucs du code que vous m'avez passé, donc j'ai remis ceci :
Pour un résultat identique, c'est à dire que je me retrouve toujours avec des doublons et apparemment un nombre aléatoire entre chaque lancement de l'App.
Le JSON est toujours le même, donc toujours les mêmes objets, les seuls éléments qui peuvent être différent sont le id (de type NSString) de chaque objet, c'est pour ça que je ne teste que ça.
Sinon, dans le code de la UITableViewController, j'ai ceci :
Voilà , en espérant n'avoir oublié aucun détails.
En fait c'est moi qui lui ai passé cette exemple oui en fait c'est plutôt [second getID] ou second.id !!
Erreur de frappe quand j'ai voulu donner l'exemple
Non je n'ai rien dit j'avais écris ça précédemment :
myID est une méthode qui retourne l'id mais tu peux passer directement par self.id isEqualToString second.myID
Alors je viens de faire un test, que ce soit ça :
ou :
Les deux sont possibles mais le résultat est identique (toujours des doubles). A moins que j'ai rien compris à vos messages ?
Tu pourrais faire un printf avant de quitter la méthode equals et dire
Et poster le résultat
Bon alors, c'est un peu la fête au Moulin Rouge. Déjà je suis reparti sur ton code, histoire d'être un peu plus sur, voici :
Voici un résultat :
Oui, oui, c'est un résultat
Un autre :
Et un dernier pour la route :
Alors montre moi comment est déclaré ton id déjà
Ensuite utilise tu isEqual ou iEqualToString ?
Fait attention NSString *id ne PEUT EXISTER !!!!
id correspond à un objet comme dans - (id) init
Alors déclare ton id sous la forme : NSString *identification;
Ca t'évitera les problèmes. Car la je crois qu'il compare autre chose que les string mais plutôt leur classe ...
Non j'ai changé le nom c'est is_unTruc, pas juste id justement pour en pas poser ce genre de problème.
Voici sa déclaration :
isEqualToString: pour répondre à ta question.
EDIT : dans mon fichier m j'ai ceci :
Je viens de faire ça :
Avant le if qui compare les chaines de caractère et retour : 0, rien, nada.
Sur un autre essai, sans rien changé, juste arrêt du simulateur et relance d'un autre test, il m'affiche des résultats où là , j'ai les deux :
Non mais je viens de comprendre... Je viens d'obtenir ça :
Donc, il récupère les données de manières aléatoire because c'est du asynchrone. Le problème, et c'est ce que j'essayais de vous expliquer plus haut, je suis dans une boucle où j'ajoutes mes objets à un tableau.
Mais quand un nouvel objet arrive, il faut parcourir le tableau pour voir si y a pas déjà un double. Avec la méthode actuelle, on ne le fais pas, donc c'est au bonheur la chance.
Par contre, aller parcourir dans ce tableau maintenant je pense pas que ce soit une bonne idée.
Qu'en pensez-vous ? (et est-ce que je suis assez clair ? )
Parcourir le tableau à chaque fois est hyper couteux non ?
Tu peux faire : containsObject avant d'insérer (par contre comme dit plus haut le isEqual doit être implémenté)
The containsObject method will invoke isEqual: on the underlying objects being compared.
Unless you implement isEqual: in the SUPDataValueList object, it will simply do a pointer comparison which is the default behavior of isEqual in NSObject.
Ou -[NSArray indexOfObjectIdenticalTo:]
Beh oui, je pense aussi. Mais comment faire alors quand il est plein ?