Как удалить дубликаты в хеше в 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]".... Создание электронной почты ключом, а не другим поля?
Ответы
Ответ 1
Я знаю, что это старый поток, но 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
Объединить строки с одинаковым идентификатором электронной почты.
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
В Ruby 1.9.2 Array#uniq
будет принимать блок-параметр, который он будет использовать при сравнении ваших объектов:
arrays.uniq { |h| h[:email] }
Ответ 3
Если вы помещаете это прямо в базу данных, просто используйте 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
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 }
Ответ 4
Хорошо, это (удалить дубликаты) - это то, о чем вы просили:
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"}]