2008-10-08 103 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}] 
相關問題