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

StackOverflow https://stackoverflow.com/questions/181091

  •  05-07-2019
  •  | 
  •  

質問

ハッシュの配列があり、そこから一意の値が必要です。 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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top