2011-03-06 53 views
13

我有一個哈希值,像這樣:如何在Ruby on Rails中刪除哈希中的重複項?

[ 
    { 
    :lname => "Brown", 
    :email => "[email protected]", 
    :fname => "James" 
    }, 
    { 
    :lname => nil, 
    :email => "[email protected]", 
    :fname => nil 
    }, 
    { 
    :lname => "Smith", 
    :email => "[email protected]", 
    :fname => "Brad" 
    }, 
    { 
    :lname => nil, 
    :email => "[email protected]", 
    :fname => nil 
    }, 
    { 
    :lname => "Smith", 
    :email => "[email protected]", 
    :fname => "Brad" 
    }, 
    { 
    :lname => nil, 
    :email => "[email protected]", 
    :fname => nil 
    } 
] 

我想什麼來學習如何做的是如何刪除一條記錄,如果它是重複的。意思是,看看有多少個「[email protected]」,我怎麼去除重複的記錄,意味着刪除所有其他有「[email protected]」電子郵件的人......使電子郵件不是其他的字段?

+2

是A純Ruby散列或數據庫中的實際數據表示散列(比如,通過ActiveRecord的)? – 2011-03-06 02:56:32

+1

爲什麼不把validates_uniqueness_of電子郵件字段?這樣即使你的參數中有重複的東西,它也不會被保存。還會在保存失敗時發生必要的錯誤捕獲。 – corroded 2011-03-06 02:56:47

+0

它是基於CSV列表創建的,用戶可以在其中輸入電子郵件來邀請朋友 – AnApprentice 2011-03-06 02:57:40

回答

16

我知道這是一個古老的線程,但Rails有對「可枚舉」叫「index_by」,可在這種情況下是很方便的方法:

list = [ 
    { 
    :lname => "Brown", 
    :email => "[email protected]", 
    :fname => "James" 
    }, 
    { 
    :lname => nil, 
    :email => "[email protected]", 
    :fname => nil 
    }, 
    { 
    :lname => "Smith", 
    :email => "[email protected]", 
    :fname => "Brad" 
    }, 
    { 
    :lname => nil, 
    :email => "[email protected]", 
    :fname => nil 
    }, 
    { 
    :lname => "Smith", 
    :email => "[email protected]", 
    :fname => "Brad" 
    }, 
    { 
    :lname => nil, 
    :email => "[email protected]", 
    :fname => nil 
    } 
] 

現在你可以得到唯一的行如下:

list.index_by {|r| r[:email]}.values 

合併具有相同電子郵件ID的行。

list.group_by{|r| r[:email]}.map do |k, v| 
    v.inject({}) { |r, h| r.merge(h){ |key, o, n| o || n } } 
end 

自定義,但有效的方法:

list.inject({}) do |r, h| 
    (r[h[:email]] ||= {}).merge!(h){ |key, old, new| old || new } 
    r 
end.values 
5

如果您將此直接放入數據庫中,請在模型中使用validates_uniqueness_of :email。請參閱documentation for this

如果需要被使用,那麼之前從實際哈希刪除它們做:

emails = [] # This is a temporary array, not your results. The results are still in my_array 
my_array.delete_if do |item| 
    if emails.include? item[:email] 
    true 
    else 
    emails << item[:email] 
    false 
    end 
end 

UPDATE

這將合併重複條目

merged_list = {} 
my_array.each do |item| 
    if merged_list.has_key? item[:email] 
    merged_list[item.email].merge! item 
    else 
    merged_list[item.email] = item 
    end 
end 
my_array = merged_list.collect { |k, v| v } 
+0

謝謝,但這將如何工作。我不想失去所有其他信息。我想採取上面的散列,並刪除重複同時保留fname和lname。 – AnApprentice 2011-03-06 03:07:46

+2

所以你真的想用同一個電子郵件地址_merge_條目?這與刪除重複項不同,這就是你所要求的。 – 2011-03-06 03:10:19

+0

不合並只是根據電子郵件的關鍵刪除任何重複的。它可以是非智能的,只需要冷杉[email protected],然後刪除其餘的,如果任何重複僅基於電子郵件存在。 – AnApprentice 2011-03-06 03:19:17

21

的內容在Ruby 1.9.2中,Array#uniq將接受一個塊參數,它將在比較對象時使用它:

arrays.uniq { |h| h[:email] } 
+0

拍攝我不是紅寶石1.9.2 – AnApprentice 2011-03-06 03:19:59

+0

@AnApprentice您可以使用backports gem和'require'backports/1.9.2/array/uniq''。 – 2013-03-23 17:39:24

1

好吧,這(刪除重複的)是你的要求爲:

a.sort_by { |e| e[:email] }.inject([]) { |m,e| m.last.nil? ? [e] : m.last[:email] == e[:email] ? m : m << e } 

但我認爲這(合併值)是你想要什麼:

a.sort_by { |e| e[:email] }.inject([]) { |m,e| m.last.nil? ? [e] : m.last[:email] == e[:email] ? (m.last.merge!(e) { |k,o,n| o || n }; m) : m << e } 

也許我因爲有不同的格式和測試用例:

Aiko:so ross$ cat mergedups 
require 'pp' 

a = [{:fname=>"James", :lname=>"Brown", :email=>"[email protected]"}, 
    {:fname=>nil,  :lname=>nil,  :email=>"[email protected]"}, 
    {:fname=>"Brad", :lname=>"Smith", :email=>"[email protected]"}, 
    {:fname=>nil,  :lname=>nil,  :email=>"[email protected]"}, 
    {:fname=>"Brad", :lname=>"Smith", :email=>"[email protected]"}, 
    {:fname=>"Brad", :lname=>"Smith", :email=>"[email protected]"}] 

pp(
    a.sort_by { |e| e[:email] }.inject([]) do |m,e| 
    m.last.nil? ? [e] : 
     m.last[:email] == e[:email] ? (m.last.merge!(e) { |k,o,n| o || n }; m) : 
     m << e 
    end 
) 
Aiko:so ross$ ruby mergedups 
[{:email=>"[email protected]", :fname=>"Brad", :lname=>"Smith"}, 
{:email=>"[email protected]", :fname=>"James", :lname=>"Brown"}] 
+0

這是時髦,只希望我知道它是如何做它做的。對於額外的點有點評論 – AnApprentice 2011-03-06 03:38:13

+0

'.inject([])'做了什麼? – 2011-03-06 03:38:27

+2

@AnApprentice:當然沒問題。 '#inject'是'Enumerable'中的一個方法,由'Array'實現。在這種形式中,它遍歷數組產生一個* memo *和* element *對象到該塊,該塊返回下一個迭代的*備忘錄*。所以,在sort_by之後,我只是將每個散列與最後一個備忘錄中的最後一個進行比較,如果電子郵件匹配,則合併這些字段,否則我只是將該元素粘貼到備忘錄的末尾,最終是'inject'將返回的內容作爲表達式的值。 – DigitalRoss 2011-03-06 03:41:52

相關問題