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

时间:2011-03-06 02:54:06

标签: ruby-on-rails ruby ruby-on-rails-3

我有这样的哈希:

[
  {
    :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”的人....将电子邮件作为密钥而不是其他字段?

4 个答案:

答案 0 :(得分:22)

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

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

答案 1 :(得分:17)

我知道这是一个旧线程,但是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

答案 2 :(得分: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

<强>更新

这将合并重复条目的内容

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 }

答案 3 :(得分: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=>"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"}]