如何在Ruby on Rails中删除散列中的重复项?

我有这样的哈希:

[ { :lname => "Brown", :email => "james@intuit.com", :fname => "James" }, { :lname => nil, :email => "brad@intuit.com", :fname => nil }, { :lname => "Smith", :email => "brad@intuit.com", :fname => "Brad" }, { :lname => nil, :email => "brad@intuit.com", :fname => nil }, { :lname => "Smith", :email => "brad@intuit.com", :fname => "Brad" }, { :lname => nil, :email => "brad@intuit.com", :fname => nil } ] 

我想学习怎么做是如何删除记录,如果它是重复的。 意思是,看看有几个“brad@intuit.com”如何删除重复记录,意味着删除所有其他有电子邮件“brad@intuit.com”的人….将电子邮件作为密钥而不是其他字段?

我知道这是一个旧线程,但是Rails在’Enumerable’上有一个名为’index_by’的方法,在这种情况下可以很方便:

 list = [ { :lname => "Brown", :email => "james@intuit.com", :fname => "James" }, { :lname => nil, :email => "brad@intuit.com", :fname => nil }, { :lname => "Smith", :email => "brad@intuit.com", :fname => "Brad" }, { :lname => nil, :email => "brad@intuit.com", :fname => nil }, { :lname => "Smith", :email => "brad@intuit.com", :fname => "Brad" }, { :lname => nil, :email => "brad@intuit.com", :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 

在Ruby 1.9.2中, Array#uniq将接受一个块参数,它将在比较对象时使用:

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

如果您将其直接放入数据库,只需在模型中使用validates_uniqueness_of :email 。 请参阅相关文档 。

如果您在使用之前需要从实际哈希中删除它们,请执行以下操作:

 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 

更新

这将合并重复条目的内容

 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 } 

好的,这个(删除重复项)是你要求的:

 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=>"james@intuit.com"}, {:fname=>nil, :lname=>nil, :email=>"brad@intuit.com"}, {:fname=>"Brad", :lname=>"Smith", :email=>"brad@intuit.com"}, {:fname=>nil, :lname=>nil, :email=>"brad@intuit.com"}, {:fname=>"Brad", :lname=>"Smith", :email=>"brad@intuit.com"}, {:fname=>"Brad", :lname=>"Smith", :email=>"brad@intuit.com"}] 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=>"brad@intuit.com", :fname=>"Brad", :lname=>"Smith"}, {:email=>"james@intuit.com", :fname=>"James", :lname=>"Brown"}]