Question

J'ai un tableau de hachages et je veux en retirer les valeurs uniques. L'appel de Array.uniq ne me donne pas ce à quoi je m'attends.

a = [{:a => 1},{:a => 2}, {:a => 1}]
a.uniq # => [{:a => 1}, {:a => 2}, {:a => 1}]

Où je m'attendais:

[{:a => 1}, {:a => 2}]

En cherchant sur le net, je n’ai pas trouvé de solution qui me satisfasse. Les gens ont recommandé de redéfinir Hash.eql? et Hash.hash , car c’est ce que Array.uniq interroge.

Modifier: Là où je me suis retrouvé dans le monde réel, les hachages étaient légèrement plus complexes. Ils étaient le résultat d'analyses JSON comportant plusieurs champs, dont certaines valeurs étaient également des hachages. J'ai eu un tableau de ces résultats que je voulais filtrer les valeurs uniques.

Je n'aime pas les solutions Hash.eql? et Hash.hash redéfinies, car je devrais soit redéfinir Hash globalement , ou redéfinissez-le pour chaque entrée de mon tableau. Changer la définition de Hash pour chaque entrée serait fastidieux, d’autant plus qu’il peut y avoir des hachages imbriqués dans chaque entrée.

La modification globale du Hash a un potentiel, en particulier si cela était fait temporairement. Je souhaiterais créer une autre classe ou fonction d'assistance englobant la sauvegarde des anciennes définitions et leur restauration, mais je pense que cela ajoute davantage de complexité que nécessaire.

L'utilisation de inject semble être une bonne alternative à la redéfinition du Hash .

Était-ce utile?

La solution

Je peux obtenir ce que je veux en appelant injecter

a = [{:a => 1},{:a => 2}, {:a => 1}]
a.inject([]) { |result,h| result << h unless result.include?(h); result }

Ceci retournera:

[{:a=>1}, {:a=>2}]

Autres conseils

Ruby 1.8.7+ renverra exactement ce que vous attendiez:

[{:a=>1}, {:a=>2}, {:a=>1}].uniq
#=> [{:a=>1}, {:a=>2}] 

J'ai eu une situation similaire, mais les hashes avaient des clés. J'ai utilisé la méthode de tri.

Ce que je veux dire:

vous avez un tableau:

[{:x=>1},{:x=>2},{:x=>3},{:x=>2},{:x=>1}]

vous le triez ( #sort_by {| t | t [: x]} ) et obtenez ceci:

[{:x=>1}, {:x=>1}, {:x=>2}, {:x=>2}, {:x=>3}]

maintenant une version légèrement modifiée de la réponse d'Aaaron Hinni:

your_array.inject([]) do |result,item| 
  result << item if !result.last||result.last[:x]!=item[:x]
  result
end

J'ai aussi essayé:

test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]}

mais c'est très lent. voici mon repère:

test=[]
1000.times {test<<{:x=>rand}}

Benchmark.bmbm do |bm|
  bm.report("sorting: ") do
    test.sort_by {|t| t[:x]}.inject([]) {|r,h| r<<h if !r.last||r.last[:x]!=h[:x]; r}
  end
  bm.report("inject: ") {test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]} }
end

résultats:

Rehearsal ---------------------------------------------
sorting:    0.010000   0.000000   0.010000 (  0.005633)
inject:     0.470000   0.140000   0.610000 (  0.621973)
------------------------------------ total: 0.620000sec

                user     system      total        real
sorting:    0.010000   0.000000   0.010000 (  0.003839)
inject:     0.480000   0.130000   0.610000 (  0.612438)

En supposant que vos hachages soient toujours des paires clé-valeur uniques, cela fonctionnera:

a.map {|h| h.to_a[0]}.uniq.map {|k,v| {k => v}}

Hash.to_a crée un tableau de tableaux de valeurs-clés, de sorte que la première carte vous renvoie:

[[:a, 1], [:a, 2], [:a, 1]]

Uniq on Arrays fait ce que vous voulez en vous donnant:

[[:a, 1], [:a, 2]]

puis la deuxième carte les reconstitue comme des hachages.

Vous pouvez utiliser (testé dans Ruby 1.9.3),

[{a: 1},{a: 2},{a:1}].uniq => [{a:1},{a: 2}]
[{a: 1,b: 2},{a: 2, b: 2},{a: 1, b: 3}].uniq_by {|v| v[:a]} => [{a: 1,b: 2},{a: 2, b: 2}]

La réponse que vous donnez est similaire à celle décrite ici . Il remplace les méthodes hash et eql? sur les hachages devant apparaître dans le tableau, ce qui fait que uniq se comporte correctement.

La méthode de canal sur les tableaux (disponible depuis le 1.8.6) effectue l'union d'un ensemble (retourne un tableau), voici donc un autre moyen possible d'obtenir des éléments uniques de tout tableau a :

[] | un

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top