如何从Ruby中的散列数组中获得惟一的元素?

时间:2021-07-02 14:29:57

I have an array of hashes, and I want the unique values out of it. Calling Array.uniq doesn't give me what I expect.

我有一个散列数组,我想从中得到唯一的值。调用数组。uniq没有给我我想要的。

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

Where I expected:

我期望:

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

In searching around on the net, I didn't come up with a solution that I was happy with. Folks recommended redefining Hash.eql? and Hash.hash, since that is what Array.uniq is querying.

在网上搜索时,我并没有找到一个让我满意的解决方案。人建议重新定义Hash.eql吗?和散列。哈希,因为那是数组。uniq查询。

Edit: Where I ran into this in the real world, the hashes were slightly more complex. They were the result of parsed JSON that had multiple fields, some of which the values were hashes as well. I had an array of those results that I wanted to filter out the unique values.

编辑:在现实世界中,我遇到了这个问题。它们是解析JSON的结果,该JSON具有多个字段,其中一些值也是散列。我有一个结果数组我想要过滤掉唯一的值。

I don't like the redefine Hash.eql? and Hash.hash solution, because I would either have to redefine Hash globally, or redefine it for each entry in my array. Changing the definition of Hash for each entry would be cumbersome, especially since there may be nested hashes inside of each entry.

我不喜欢重新定义Hash.eql?和散列。哈希解决方案,因为我要么全局重新定义哈希,要么为数组中的每个条目重新定义哈希。更改每个条目的散列定义会很麻烦,特别是因为每个条目内部可能有嵌套的散列。

Changing Hash globally has some potential, especially if it were done temporarily. I'd want to build another class or helper function that wrapped saving off the old definitions, and restoring them, but I think this adds more complexity than is really needed.

全局改变散列有一些潜力,特别是如果它是临时完成的。我想构建另一个类或助手函数,它将保存旧定义并恢复它们,但是我认为这增加了比实际需要的更多的复杂性。

Using inject seems like a good alternative to redefining Hash.

使用注入似乎是重新定义散列的一个好选择。

8 个解决方案

#1


27  

I can get what I want by calling inject

我可以通过调用注入来得到我想要的

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

This will return:

这将返回:

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

#2


17  

Ruby 1.8.7+ will return just what you have expected:

Ruby 1.8.7+将返回您所期望的:

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

#3


5  

I've had a similar situation, but hashes had keys. I used sorting method.

我也遇到过类似的情况,但是散列有键。我使用的排序方法。

What I mean:

我的意思:

you have an array:

你有一个数组:

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

you sort it (#sort_by {|t| t[:x]}) and get this:

你把它排序(#sort_by {|t [:x]}),然后得到:

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

now a bit modified version of answer by Aaaron Hinni:

Aaaron Hinni修改过的回答:

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

I've also tried:

我也试过:

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

but it's very slow. here is my benchmark:

但这是非常缓慢。这是我的基准:

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

results:

结果:

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)

#4


3  

Assuming your hashes are always single key-value pairs, this will work:

假设您的散列始终是单个键值对,那么这将起作用:

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

Hash.to_a creates an array of key-value arrays, so the first map gets you:

散列。to_a创建一个键值数组数组,因此第一个映射将获得:

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

uniq on Arrays does what you want, giving you:

uniq on array做你想做的事,给你:

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

and then the second map puts them back together as hashes again.

然后第二张图将它们重新组合为散列。

#5


1  

You can use (tested in ruby 1.9.3),

您可以使用(在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}]

#6


0  

The answer you give is similar to the one discussed here. It overrides the hash and eql? methods on the hashes that are to appear in the array which then makes uniq behave correctly.

你给出的答案与这里讨论的答案相似。它重写散列和eql?将出现在数组中的散列上的方法,然后使uniq正确运行。

#7


0  

found on google http://mikeburnscoder.wordpress.com/2008/01/18/uniquify-an-array-of-hashes-in-ruby/

在谷歌找到http://mikeburnscoder.wordpress.com/2008/01/18/uniquify-an-array-of-hashes-in-ruby/

#8


0  

The pipe method on arrays (available since 1.8.6) performs set union (returning an array), so the following is another possible way to get unique elements of any array a:

数组上的pipe方法(从1.8.6开始可用)执行set union(返回一个数组),因此下面是获得任意数组a的惟一元素的另一种可能的方法:

[] | a

[]|

#1


27  

I can get what I want by calling inject

我可以通过调用注入来得到我想要的

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

This will return:

这将返回:

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

#2


17  

Ruby 1.8.7+ will return just what you have expected:

Ruby 1.8.7+将返回您所期望的:

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

#3


5  

I've had a similar situation, but hashes had keys. I used sorting method.

我也遇到过类似的情况,但是散列有键。我使用的排序方法。

What I mean:

我的意思:

you have an array:

你有一个数组:

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

you sort it (#sort_by {|t| t[:x]}) and get this:

你把它排序(#sort_by {|t [:x]}),然后得到:

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

now a bit modified version of answer by Aaaron Hinni:

Aaaron Hinni修改过的回答:

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

I've also tried:

我也试过:

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

but it's very slow. here is my benchmark:

但这是非常缓慢。这是我的基准:

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

results:

结果:

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)

#4


3  

Assuming your hashes are always single key-value pairs, this will work:

假设您的散列始终是单个键值对,那么这将起作用:

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

Hash.to_a creates an array of key-value arrays, so the first map gets you:

散列。to_a创建一个键值数组数组,因此第一个映射将获得:

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

uniq on Arrays does what you want, giving you:

uniq on array做你想做的事,给你:

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

and then the second map puts them back together as hashes again.

然后第二张图将它们重新组合为散列。

#5


1  

You can use (tested in ruby 1.9.3),

您可以使用(在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}]

#6


0  

The answer you give is similar to the one discussed here. It overrides the hash and eql? methods on the hashes that are to appear in the array which then makes uniq behave correctly.

你给出的答案与这里讨论的答案相似。它重写散列和eql?将出现在数组中的散列上的方法,然后使uniq正确运行。

#7


0  

found on google http://mikeburnscoder.wordpress.com/2008/01/18/uniquify-an-array-of-hashes-in-ruby/

在谷歌找到http://mikeburnscoder.wordpress.com/2008/01/18/uniquify-an-array-of-hashes-in-ruby/

#8


0  

The pipe method on arrays (available since 1.8.6) performs set union (returning an array), so the following is another possible way to get unique elements of any array a:

数组上的pipe方法(从1.8.6开始可用)执行set union(返回一个数组),因此下面是获得任意数组a的惟一元素的另一种可能的方法:

[] | a

[]|