2008-10-08 69 views
24

我有散列的数组,我想的唯一值出来。拨打Array.uniq不会给我我所期望的。我如何从哈希在Ruby中数组的独特元素?

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的好替代方案。

回答

27

我能得到我想要的东西通过调用inject

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

这将返回:

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

更多更好,我觉得不是一个链接我张贴以上 – edthix 2009-03-06 10:30:38

0

你给出的答案是类似的一个讨论here。它覆盖了要在阵列中出现的散列的hasheql?方法,然后uniq表现正确。

+0

这是我在网上找到的解决方案之一。我不喜欢我需要重新定义哈希,只是为了调用uniq。 – 2008-10-08 17:41:43

+0

如果香草哈希和数组类没有做你需要的,你应该真的考虑定义你自己的实现所需行为的类。 你能描述一下你在用散列数组来模拟什么? – 2008-10-09 02:17:33

2

假设你的哈希总是单一的键值对,这将工作:

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

,然后第二个地图把他们带回磕磕碰碰呃再次散列。

+0

我遇到的真实世界问题使用了更复杂的哈希。 – 2008-10-08 17:39:15

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

红宝石1.8.7+将返回刚才你所预期的:因为1.8

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

阵列上的管法(可用.6)执行set union(返回一个数组),所以下面是获取任何数组的唯一元素的另一种可能方式:a

[] | a

1

您可以使用(在红宝石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}] 
相关问题