web-dev-qa-db-ja.com

Rubyのハッシュの配列から一意の要素を取得するにはどうすればよいですか?

ハッシュの配列があり、そこから一意の値が必要です。 Array.uniqを呼び出しても、期待どおりの結果が得られません。

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

私が期待したところ:

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

ネットで検索してみたところ、満足できる解決策が思いつかなかった。 Hash.eql?がクエリを実行しているのでHash.hashArray.uniqを再定義することを人々は推奨しました。

編集:私が現実の世界でこれに遭遇したところ、ハッシュは少し複雑でした。これらは、複数のフィールドを持つ解析されたJSONの結果であり、その一部の値もハッシュでした。一意の値を除外したい結果の配列がありました。

Hash.eql?Hash.hashのソリューションを再定義するのは好きではありません。なぜなら、Hashをグローバルに再定義するか、配列のエントリごとに再定義する必要があるからです。各エントリのHashの定義を変更することは、特に各エントリ内にネストされたハッシュがある可能性があるため、面倒です。

Hashをグローバルに変更することは、特に一時的に行われた場合、ある程度の可能性があります。古い定義を保存して復元することをラップした別のクラスまたはヘルパー関数を作成したいのですが、これにより、実際に必要なものよりも複雑になると思います。

injectを使用することは、Hashを再定義する代わりの良い方法のようです。

24
Aaron Hinni

injectを呼び出すと、必要なものを取得できます

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

これは戻ります:

[{:a=>1}, {:a=>2}]
27
Aaron Hinni

Ruby 1.8.7+は、期待どおりの結果を返します。

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

私も同様の状況にありましたが、ハッシュには鍵がありました。ソート方法を使用しました。

私が意味したのは:

あなたは配列を持っています:

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

並べ替えます(#sort_by {|t| t[:x]})そしてこれを入手してください:

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

aaaron Hinniによる回答の少し変更されたバージョンになりました:

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

私も試しました:

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

しかし、それは非常に遅いです。これが私のベンチマークです:

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

結果:

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)
5
naquad

ハッシュが常に単一のキーと値のペアであると仮定すると、これは機能します。

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

Hash.to_aは、Key-Value配列の配列を作成するため、最初のマップで次のことがわかります。

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

uniq on Arraysはあなたが望むことを行い、あなたに以下を与えます:

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

次に、2番目のマップはそれらをハッシュとして再びまとめます。

3
glenn mcdonald

使用できます(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}]
1
shajin
0
edthix

あなたが与える答えは議論されたものに似ています ここ 。これは、配列に表示されるハッシュのhashメソッドとeql?メソッドをオーバーライドし、uniqを正しく動作させます。

0
Mark Reid

配列のpipeメソッド(1.8.6以降で使用可能)はset union(配列を返す)を実行するため、配列aの一意の要素を取得する別の可能な方法は次のとおりです。

[] | a

0
yoniLavi