Rubyのハッシュの配列から一意の要素を取得するにはどうすればよいですか?
質問
ハッシュの配列があり、そこから一意の値が必要です。 Array.uniq
を呼び出しても、期待したものが得られません。
a = [{:a => 1},{:a => 2}, {:a => 1}]
a.uniq # => [{:a => 1}, {:a => 2}, {:a => 1}]
予想した場所:
[{:a => 1}, {:a => 2}]
ネット上で検索しても、満足できる解決策は思いつきませんでした。 Array.uniq
がクエリを実行しているので、 Hash.eql?
と Hash.hash
を再定義することをお勧めします。
編集: 現実の世界でこれに遭遇した場合、ハッシュは少し複雑でした。これらは、複数のフィールドを持つJSONの解析結果であり、値の一部はハッシュでもありました。一意の値を除外したい結果の配列がありました。
Hash.eql?
および Hash.hash
のソリューションを再定義するのは好ましくありません。 Hash
をグローバルに再定義する必要があるためです。 、または配列内のエントリごとに再定義します。特に各エントリ内にネストされたハッシュがあるため、各エントリの Hash
の定義を変更するのは面倒です。
ハッシュ
をグローバルに変更すると、特に一時的に行われた場合は、ある程度の可能性があります。古い定義を保存して元に戻す別のクラスまたはヘルパー関数を作成したいのですが、これは本当に必要なものよりも複雑になると思います。
inject
の使用は、 Hash
の再定義に代わる優れた方法のようです。
解決
inject
a = [{:a => 1},{:a => 2}, {:a => 1}]
a.inject([]) { |result,h| result << h unless result.include?(h); result }
これは以下を返します:
[{:a=>1}, {:a=>2}]
他のヒント
Ruby 1.8.7+は期待どおりの結果を返します:
[{:a=>1}, {:a=>2}, {:a=>1}].uniq
#=> [{:a=>1}, {:a=>2}]
同様の状況がありましたが、ハッシュにはキーがありました。ソート方法を使用しました。
意味:
配列があります:
[{: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)
ハッシュが常に単一のキーと値のペアであると仮定すると、これは機能します:
a.map {|h| h.to_a[0]}.uniq.map {|k,v| {k => v}}
Hash.to_aはキーと値の配列の配列を作成するので、最初のマップはあなたを取得します:
[[:a, 1], [:a, 2], [:a, 1]]
uniq on arrayはあなたが望むことをします:
[[:a, 1], [:a, 2]]
そして2番目のマップはそれらを再びハッシュとして元に戻します。
使用できます(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}]
あなたが与える答えは、こちら。配列に表示されるハッシュの hash
および eql?
メソッドをオーバーライドし、 uniq
が正しく動作するようにします。
配列(1.8.6以降で使用可能)のパイプメソッドは、集合の和集合(配列を返す)を実行するため、以下は、配列 a
の一意の要素を取得する別の可能な方法です:
[] | a