简体   繁体   English

如何从Ruby中的哈希数组中获取唯一元素?

[英]How do I get the unique elements from an array of hashes in Ruby?

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. 调用Array.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? 人们建议重新定义Hash.eql? and Hash.hash , since that is what Array.uniq is querying. Hash.hash ,因为这是Array.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. 它们是解析的JSON的结果,它具有多个字段,其中一些值也是哈希值。 I had an array of those results that I wanted to filter out the unique values. 我有一系列的结果,我想过滤掉唯一的值。

I don't like the redefine Hash.eql? 我不喜欢重新定义Hash.eql? and Hash.hash solution, because I would either have to redefine Hash globally, or redefine it for each entry in my array. Hash.hash解决方案,因为我要么必须全局重新定义Hash ,要么为我的数组中的每个条目重新定义它。 Changing the definition of Hash for each entry would be cumbersome, especially since there may be nested hashes inside of each entry. 为每个条目更改Hash的定义会很麻烦,尤其是因为每个条目中可能存在嵌套的哈希值。

Changing Hash globally has some potential, especially if it were done temporarily. 在全球范围内改变Hash有一些潜力,特别是如果它是暂时完成的话。 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 . 使用inject似乎是重新定义Hash一个很好的选择。

I can get what I want by calling inject 我可以通过调用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}]

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}] 

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

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: Hash.to_a创建一个键值数组数组,因此第一个映射可以帮助您:

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

uniq on Arrays does what you want, giving you: uniq on Arrays做你想要的,给你:

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

and then the second map puts them back together as hashes again. 然后第二张地图再次将它们作为哈希重新组合在一起。

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}]

数组上的管道方法(自1.8.6起可用)执行set union(返回数组),因此以下是获取任何数组a唯一元素的另一种可能方法:

[] | a

The answer you give is similar to the one discussed here . 你给出的答案类似于这里讨论的答案。 It overrides the hash and eql? 它覆盖了hasheql? methods on the hashes that are to appear in the array which then makes uniq behave correctly. 哈希中出现在数组中的方法,然后使uniq正确运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM