ハッシュの配列があり、そこから一意の値が必要です。 Array.uniq
を呼び出しても、期待どおりの結果が得られません。
a = [{:a => 1},{:a => 2}, {:a => 1}]
a.uniq # => [{:a => 1}, {:a => 2}, {:a => 1}]
私が期待したところ:
[{:a => 1}, {:a => 2}]
ネットで検索してみたところ、満足できる解決策が思いつかなかった。 Hash.eql?
がクエリを実行しているのでHash.hash
とArray.uniq
を再定義することを人々は推奨しました。
編集:私が現実の世界でこれに遭遇したところ、ハッシュは少し複雑でした。これらは、複数のフィールドを持つ解析されたJSONの結果であり、その一部の値もハッシュでした。一意の値を除外したい結果の配列がありました。
Hash.eql?
とHash.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は、Key-Value配列の配列を作成するため、最初のマップで次のことがわかります。
[[:a, 1], [:a, 2], [:a, 1]]
uniq on Arraysはあなたが望むことを行い、あなたに以下を与えます:
[[: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
を正しく動作させます。
配列のpipeメソッド(1.8.6以降で使用可能)はset union(配列を返す)を実行するため、配列a
の一意の要素を取得する別の可能な方法は次のとおりです。
[] | a