Croiser 2 arrays de dictionnaires

muqaddarmuqaddar Administrateur
Salut,

Est-ce que quelqu'un a une astuce pour croiser 2 arrays de dictionnaires afin de vérifier l'équivalence de valeurs de clés ?

Exemple :

array1 contient 100 dictionnaires de 2 clés/valeurs (x, y)
array2 contient 10 dictionnaires de 2 clés/valeurs (x, y)

On veut vérifier que (array1.x == array2.x et array1.y == array2.y).

On peut énumérer array1 et énumérer array2 à  l'intérieur et comparer les valeurs des clés...
Seulement, bonjour les perfs je pense...

Peut-être créer 2 tableaux temporaires pour comparer autrement (containsObject ?) ? Afin de récupérer un index dans le tableau original.

Vos suggestions sont les bienvenues.

Réponses

  • AliGatorAliGator Membre, Modérateur
    19:48 modifié #2
    Transforme des NSArray en NSSet puis utilise les méthodes de NSSet pour faire des intersections, etc.
  • muqaddarmuqaddar Administrateur
    19:48 modifié #3
    dans 1316523251:

    Transforme des NSArray en NSSet puis utilise les méthodes de NSSet pour faire des intersections, etc.


    Ok, je vais essayer. Je me sers rarement des NSSet.
  • muqaddarmuqaddar Administrateur
    19:48 modifié #4
    Cela m'oblige à  refabriquer des NSSet en parcourant les arrays originaux quand-même puisque les dictionnaires dans les 2 arrays n'ont pas le même nombre d'objets (je ne peux pas utiliser les méthodes magiques setWithArray du coup qui me serviraient à  rien ensuite), car le but est de comparer les objets avec intersectSet ou containsObject...
  • laudemalaudema Membre
    septembre 2011 modifié #5
    Pourquoi ne pas utiliser les blocks ?
    D'accord c'est toujours parcourir mais en général les blocks ça va plutôt très vite ..
    hth
  • AliGatorAliGator Membre, Modérateur
    19:48 modifié #6
    Je ne comprend pas pourquoi le fait que tes NSArray n'ont pas le même nombre d'objets est un problème, au contraire ?!

    Si tu me dis que les objets qu'il y a dans tes NSArray (en l'occurrence tes NSDictionary (x,y)) ne sont pas directement comparables par isEqual car en fait n'ont pas la même structure, là  c'est autre chose.

    Mais si tu as un NSArray a avec 20 paires (x,y) et un NSArray b avec 12 paires (x,y) dont certaines communes avec le tableau a, alors NSSet va bien te permettre de faire cela.

    Si c'est pas le cas, alors j'ai mal compris ton problème (et un exemple serait alors le bienvenu)
  • muqaddarmuqaddar Administrateur
    19:48 modifié #7
    dans 1316533903:

    Je ne comprend pas pourquoi le fait que tes NSArray n'ont pas le même nombre d'objets est un problème, au contraire ?!


    Je plussoie.

    dans 1316533903:

    Si tu me dis que les objets qu'il y a dans tes NSArray (en l'occurrence tes NSDictionary (x,y)) ne sont pas directement comparables par isEqual car en fait n'ont pas la même structure, là  c'est autre chose.


    Voilà , c'est ça... ;-) Les dico ne sont pas les mêmes.
    D'un côté j'ai des dico à  3 clés/valeurs.
    De l'autre des dicos à  10 clés/valeurs.

    Je veux comparer seulement 1 paire de clé/valeurs entre les 2 arrays.
    Appellons-les X,Y. Ce sont des NSNumber dans les dicos.

    Dans mon cas précis, je parcoure un array1 (et dans tous les cas, celui-là , j'ai besoin de le parcourir dans mon code pour d'autres besoins). Je lui prends d'ailleurs X et Y au moment de son énumération. Disons que cet array possède 100 dicos.

    Pour des questions de performance, je ne souhaite pas faire une énumération du deuxième array dans chaque itération du premier array avec objectEnumerator. Certes le nombre d'éléments est dérisoire avec mon exemple, mais avec des doubles boucles, on arrive vite à  10000 itérations.

    Les blocks sont peut-être une piste de performance comme l'a dit laudema.
  • AliGatorAliGator Membre, Modérateur
    19:48 modifié #8
    Les blocks en soi ne changeront rien du tout.
    Les blocks, ce sont des closures. Ca n'a aucun impact sur les performances.

    Par contre, utiliser GCD ou les NSFastEnumerations (qui utilisent en général les blocks dans leur API pour manipuler plus facilement ces énumérations) c'est autre chose.

    GCD va te permettre d'exécuter des bouts de code (que tu passes effectivement en paramètre via des blocks) éventuellement en parallèle.

    Les NSFastEnumerations vont permettre de récupérer (dans tes énumérations par boucle for avec la syntaxe NSFastEnumeration) les éléments de ton tableau morceaux par morceaux (genre 5 par 5 ou quoi) sous le capot, même si ensuite chaque élément est énuméré un par un (en gros sous le capot ça récupère le N premiers d'un bloc, puis itère sur ces N éléments, puis récupère les pointeurs vers les N suivants, etc... au lieu de récupérer le 1er, le traiter, récupérer le 2e, le traiter, etc) ce qui accélère un peu les boucles dans les faits.

    Dans les deux si tes tableau ne contiennent pas des objets de même structure tu vas avoir à  itérer dans tes tableau de toute façon Ou éventuellement dériver de ton tableau de dicos à  10 clés un tableau de dicos à  2 clés avec la même structure que l'autre tableau, et faire le filtrage avec ce tableau dérivé (où là  tu pourras comparer les dicos entre un tableau et l'autre), mais bon...

    Au final je me demande si ton archi et ta structure de données est bien choisie...
  • laudemalaudema Membre
    19:48 modifié #9
    Si tu n'as que deux clefs à  tester dans un block tu ne testeras la seconde que si la première est déjà  égale ça devrait quand même aller plutôt vite. En tout cas je reste curieux du résultat si tu essayes.
  • septembre 2011 modifié #10
    Si tu connais le nom des clés X et Y, tu peux utiliser @unionOfObjects à  la limite..
    ça fait un peu chier que ça soit des simples NSDictionary.. mais si tu as la "main" dessus, ça serait plus intéressant de combiner les valeurs de X et Y à  une seule clé.
    Sinon, @unionOfObjects est ultra rapide de toute manière.

    <br />NSArray* xValuesFromFirstArray = [myFirstArray valueForKeyPath:@&quot;@unionOfObjects.x&quot;];<br />NSArray* yValuesFromFirstArray = [myFirstArray valueForKeyPath:@&quot;@unionOfObjects.y&quot;];<br /><br />NSArray* xValuesFromSecondArray = [myFirstArray valueForKeyPath:@&quot;@unionOfObjects.x&quot;];<br />NSArray* yValuesFromSecondArray = [myFirstArray valueForKeyPath:@&quot;@unionOfObjects.y&quot;];<br />...<br />
    


    Sinon, comme laudema, je serai curieux de voir les perfs avec des blocks..

    Edit: j'ai fait un test avec un de mes projet actuel, le -enumerateObjectsWithOptions:usingBlock: est plus lent qu'une fast énumération.
    Après, je suppose que ça doit dépendre de la taille de la array (j'en suis à  5000 éléments)
    À noter que je faisais une simple énumération de mon côté par contre..
    0.023s avec la méthode utilisant le block
    0.020s avec une fast enum.
    (Sur OS X... donc à  tester aussi sur iOS.)

    Ma array contient + de 5000 éléments, qui sont des dictionnaires. Chaque dico contient plusieurs clés. J'ai testé la récupération des valeurs pour la clé "album", ça met 0.003s sur OS X.
    Maintenant, j'ai testé @unionOfObjects sur 4 clés:
    <br />	[myArray valueForKeyPath:@&quot;@unionOfObjects.album&quot;];<br />	[myArray valueForKeyPath:@&quot;@unionOfObjects.artist&quot;];<br />	[myArray valueForKeyPath:@&quot;@unionOfObjects.name&quot;];<br /> 	[myArray valueForKeyPath:@&quot;@unionOfObjects.genre&quot;];<br />
    

    J'en ai pour 0.007s

    Faisons la même chose, en récupérant les albums, artists, names, et genres dans une fastenum. On se dit que ça va être plus rapide vu qu'on énumère une fois. Alors que ci-dessus on répète @unionOfObjects 4x! Totalement logique effectivement, ça met 0.003s, soit autant de temps qu'un seul @unionOfObjects.

    ça n'est donc p-e pas la meilleure solution. Mais ça dépend vraiment de la taille de ta array. Si c'est assez "petit", je pense que tu peux te simplifier la vie en faisant ça.
  • muqaddarmuqaddar Administrateur
    19:48 modifié #11
    Merci pour les pistes.

    Vous utilisez quoi comme fonctions pour mesurez les benches au miliseconde ?
  • 19:48 modifié #12
    dans 1316545879:

    Merci pour les pistes.

    Vous utilisez quoi comme fonctions pour mesurez les benches au miliseconde ?


    Moi j'instancie une NSDate avant exécution et je log le timeIntervalSinceDate: après :p
    Si vous avez mieux je suis carrément preneur!
  • AliGatorAliGator Membre, Modérateur
    19:48 modifié #13
    [tt]gettimeofday()[/tt].
  • 19:48 modifié #14
    dans 1316546744:

    [tt]gettimeofday()[/tt].

    C'est le même principe quoi.. :p
  • AliGatorAliGator Membre, Modérateur
    19:48 modifié #15
    Heu mouais pas tout à  fait quand même.

    Quand on fait du benchmark, si on commence à  polluer le benchmark par des allocations (alloc/init d'un NSDate, même si ça dure pas 10s c'est pas gratuit non plus) c'est pas comme ça que t'auras des résultats de bench exploitables ;)

    Sinon t'as l'utilitaire "time" en ligne de commande pour benchmarker le temps d'exécution d'un process du début à  la fin.
  • 19:48 modifié #16
    dans 1316548850:

    Heu mouais pas tout à  fait quand même.

    Quand on fait du benchmark, si on commence à  polluer le benchmark par des allocations (alloc/init d'un NSDate, même si ça dure pas 10s c'est pas gratuit non plus) c'est pas comme ça que t'auras des résultats de bench exploitables ;)

    Sinon t'as l'utilitaire "time" en ligne de commande pour benchmarker le temps d'exécution d'un process du début à  la fin.


    T'as pas tort.. 
  • muqaddarmuqaddar Administrateur
    19:48 modifié #17
    Merci pour l'astuce.

    Je vais essayer plusieurs pistes, mais à  ce stade de mon appli je n'ai pas encore assez d'éléments dans mes arrays pour faire des benches crédibles. A suivre dans quelque temps.
  • laudemalaudema Membre
    19:48 modifié #18
    Dans la famille NSTimeInterval il y a aussi systemUptime du processInfo qui donne les secondes depuis le départ de la machine.
Connectez-vous ou Inscrivez-vous pour répondre.