Уникальные объекты в сгруппированном хэше

У меня есть хеш, созданный .group_by метод, со строками в качестве ключей и объектами AR в качестве значений. И я хочу избавиться от объектов AR с дублированным полем (другие поля могут или не могут быть одинаковыми).

Чтобы прояснить вопрос, я создал этот фрагмент:

require 'ostruct'   

def os_new(fid); OpenStruct.new(fid: fid, rand: rand(100)); end                                                                                                                                              
a = [os_new(1), os_new(2), os_new(3)]                                                                                                                                                
b = [os_new(3), os_new(3), os_new(2)]                                                                                                                                                
c = [os_new(5), os_new(5), os_new(5), os_new(5)] 

grouped_hash = { a: a, b: b, c: c} 

grouped_hash вот это:

# {:a=>[#<OpenStruct fid=1, rand=7>, #<OpenStruct fid=2, rand=50>, #<OpenStruct fid=3, rand=13>], 
#  :b=>[#<OpenStruct fid=3, rand=51>, #<OpenStruct fid=3, rand=58>, #<OpenStruct fid=2, rand=88>], 
#  :c=>[#<OpenStruct fid=5, rand=40>, #<OpenStruct fid=5, rand=8>, #<OpenStruct fid=5, rand=45>, #<OpenStruct fid=5, rand=72>]} 

Теперь в результате я хочу хэш с уникальным fid s для каждого ключа:

# {:a=>[#<OpenStruct fid=1, rand=7>, #<OpenStruct fid=2, rand=50>, #<OpenStruct fid=3, rand=13>], 
#  :b=>[#<OpenStruct fid=3, rand=51>, #<OpenStruct fid=2, rand=88>], 
#  :c=>[#<OpenStruct fid=5, rand=40>]} 

Мое решение требует нескольких действий и выглядит некрасиво.

grouped_hash.each_value do |value|                                                                                                                                                     
  fids = []
  value.delete_if do |object|
    if fids.include? object.fid                                                                                                                                                            
      true
    else                                                                                                                                                                                   
      fids << object.fid
      false
    end                                                                                                                                                                                
  end
end

Я ищу наиболее элегантный (возможно, 1-строчный) и эффективный способ (без пересоздания хеша).

3 ответа

grouped_hash.each_value(&:uniq)

Согласно объектам AR, Rails сознательно делегирует проверки на равенство в столбец идентификаторов. Если вы хотите знать, что два объекта AR содержат одинаковые значения, сравните результат вызова #attributes для обоих.

Не совсем однострочный (хотя вы также можете написать его одной строкой):

grouped_hash.inject({}) do |hash, element|
  hash[element.first] = element.last.uniq(&:fid)
  hash
end

Объединив katafrakt и katafrakt (спасибо, ребята), я сделал это:

grouped_hash.each_value { |v| v.uniq!(&:fid) }
Другие вопросы по тегам