Как удалить дубликаты в хэше в Ruby on Rails? - PullRequest
13 голосов
/ 06 марта 2011

У меня есть хеш-код:

[
  {
    :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 ]

22 голосов
/ 06 марта 2011

В Ruby 1.9.2 Array#uniq примет параметр блока, который будет использоваться при сравнении ваших объектов:

arrays.uniq { |h| h[:email] }
17 голосов
/ 15 марта 2011

Я знаю, что это старый поток, но в 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

Чтобы объединить строки с одинаковым идентификатором электронной почты.

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 голосов
/ 06 марта 2011

Если вы помещаете это непосредственно в базу данных, просто используйте 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 }
1 голос
/ 06 марта 2011

Хорошо, это (удалить дубликаты) - это то, что вы просили:

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"}]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...