Frage

Ich habe ein Array von Hashes, und ich möchte die eindeutigen Werte aus ihm heraus. Array.uniq Aufruf nicht mir geben, was ich erwarte.

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

Wo ich erwartet hatte:

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

auf dem Netz der Suche um, ich habe nicht kommen mit einer Lösung, die ich war zufrieden mit. Leute empfohlen Hash.eql? und Hash.hash neu zu definieren, denn das ist, was Array.uniq abfragt.

Edit: Wo ich in diesen in der realen Welt lief, waren die Hashes etwas komplexer. Sie waren das Ergebnis von geparsten JSON, die mehrere Felder hatte, von denen einige die Werte Hashes auch waren. Ich hatte eine Reihe von diesen Ergebnissen, dass ich die eindeutigen Werte herauszufiltern wollte.

Ich mag es nicht, die neu definieren Hash.eql? und Hash.hash Lösung, weil ich entweder Hash global neu zu definieren müsste, oder neu zu definieren es für jeden Eintrag in meinem Array. Änderung der Definition von Hash für jeden Eintrag wäre umständlich, zumal es Hashes innerhalb jedes Eintrags verschachtelt werden können.

Ändern Hash global hat ein gewisses Potenzial, vor allem, wenn es vorübergehend durchgeführt wurden. Ich würde will eine andere Klasse oder Hilfsfunktion erstellen, die die alten Definitionen eingewickelt Speicher aus, und die Wiederherstellung sie, aber ich denke, das mehr Komplexität fügt als wirklich benötigt wird.

inject Verwendung scheint wie eine gute Alternative Hash neu zu definieren.

War es hilfreich?

Lösung

Ich kann bekommen, was ich will von inject Aufruf

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

Dies wird zurück:

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

Andere Tipps

Ruby-1.8.7+ wird wieder genau das, was Sie erwartet haben:

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

Ich habe eine ähnliche Situation gehabt, aber Hashes hatte Schlüssel. Ich benutzte Sortiermethode.

Was ich meine:

Sie haben ein Array:

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

Sie sortieren sie (#sort_by {|t| t[:x]}) und erhalten so aus:

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

jetzt ein bisschen modifizierte Version Antwort von Aaaron Hinni:

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

Ich habe auch versucht:

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

, aber es ist sehr langsam. hier ist mein Maßstab:

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

Ergebnisse:

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)

Angenommen, Ihre Hashes sind immer einzelne Schlüssel-Wert-Paare, dies funktionieren wird:

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

Hash.to_a erstellt ein Array von Schlüssel-Wert-Arrays, so dass die erste Karte, die Sie bekommt:

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

uniq auf Arrays tut, was Sie wollen, geben Sie:

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

und dann die zweite Karte setzt sie wieder zusammen als Hashes wieder.

Sie können mit (getestet in 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}]

Die Antwort, die Sie geben, ist ähnlich dem diskutierten hier . Es überschreibt die hash und eql? Methoden auf die Hash-Werte, die in der Matrix erscheinen sollen, die dann richtig uniq verhalten macht.

Die Rohrverfahren auf Arrays (verfügbar seit 1.8.6) führen gesetzt Union (zurückkehr ein Array), so dass der folgende ist eine weitere mögliche Art und Weise einzigartige Elemente jeder Array a zu erhalten:

[] | a

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top