25

我有一个哈希数组,我想要其中的唯一值。打电话Array.uniq给我的不是我所期望的。

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

我期望的地方:

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

在网上四处搜索,我没有想出一个我满意的解决方案。人们建议重新定义Hash.eql?and Hash.hash,因为那Array.uniq是查询的内容。

编辑:我在现实世界中遇到这个问题时,哈希值稍微复杂一些。它们是具有多个字段的解析 JSON 的结果,其中一些值也是哈希值。我有一系列想要过滤掉唯一值的结果。

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

全球变化Hash具有一定的潜力,特别是如果它是暂时完成的。我想构建另一个类或辅助函数来包装保存旧定义并恢复它们,但我认为这增加了比实际需要更多的复杂性。

使用inject似乎是重新定义Hash.

4

8 回答 8

27

我可以通过调用得到我想要的inject

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

这将返回:

[{:a=>1}, {:a=>2}]
于 2008-10-08T01:44:15.353 回答
18

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

[{:a=>1}, {:a=>2}, {:a=>1}].uniq
#=> [{:a=>1}, {:a=>2}] 
于 2011-04-11T19:27:57.213 回答
5

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

我的意思是说:

你有一个数组:

[{: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)
于 2009-05-07T13:18:24.247 回答
3

假设您的哈希始终是单个键值对,这将起作用:

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

Hash.to_a 创建一个键值数组的数组,所以第一个映射让你:

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

数组上的 uniq 做你想做的事,给你:

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

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

于 2008-10-08T16:35:11.250 回答
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}]
于 2015-01-05T08:49:26.047 回答
0

您给出的答案与此处讨论的答案相似。它覆盖了要出现在数组中的散列上的hashand方法,然后使行为正确。eql?uniq

于 2008-10-08T04:52:36.330 回答
0

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

于 2009-03-06T10:29:41.330 回答
0

数组上的 pipe 方法(自 1.8.6 起可用)执行集合并集(返回数组),因此以下是获取任何数组的唯一元素的另一种可能方法a

[] | a

于 2012-11-07T16:49:27.273 回答