Сравнение двух похожих хэшей в ruby

Я использую ruby ​​1.8.7, и мне нужно сравнить два хэша, которые у меня есть, которые по сути являются атрибутами модели. Хэш A меньше, чем хэш B, а хэш B имеет все атрибуты хеша A, плюс некоторые дополнительные атрибуты, которые мне не нужны. Моя главная цель — увидеть, совпадают ли элементы A с соответствующими элементами B. Так, например,

@hash_a = {:cat => 'Bubby', :dog => 'Gizmo'}  
@hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'}  
@hash_a == @hash_b
#=> true

Теперь это становится немного сложнее, потому что поля не совпадают полностью, даже если они ссылаются на один и тот же фрагмент информации.

@hash_a = {:cats_name => 'Bubby', :dog => 'Gizmo'}  
@hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'}  
@hash_a == @hash_b
#=> true

Я работаю над процессом, который сравнивает два совпадающих элемента, обновляет их, если поля изменились, и только если они изменились. Или создает новый элемент, если он не может найти соответствующий элемент. Изменение имен самого хэша не вариант. В настоящее время я просто сравниваю каждое поле в частном методе, чтобы увидеть, равны ли они.

return hash_a[:cat] == hash_b[:cats_name] && hash_a[:dog] == hash_b[:dog] 

Я чувствую, что должен быть лучший способ, я ищу что-то более быстрое и элегантное, чем это.


person Mysrt    schedule 21.09.2010    source источник
comment
Чего вы хотите достичь? Простота кода сравнения? Эффективность/скорость?   -  person GreyCat    schedule 02.12.2010


Ответы (4)


Если вы преобразуете хэши в массив, вы можете сравнить их следующим образом.

@hash_a.to_a == (@hash_a.to_a & @hash_b.to_a)

Вы также можете скрыть этот код за методом в хеш-классе, если хотите:

class Hash
  def diff_equal(other)
    to_a == (to_a & other.to_a)
  end
end

Затем используйте его как @hash_a.diff_equal(@hash_b). Если вы выбрали этот путь, вы можете проверить, что другой является хешем или отвечает на метод to_a.

person Peter Brown    schedule 21.09.2010
comment
Это можно еще упростить. (a-b) возвращает пустой массив, если a является подмножеством b. stackoverflow.com/a/3897535/566878 - person Ankush; 20.03.2014

Вот как бы я это сделал:

def eql hash1, hash2, rewire = {}
  map = Hash.new {|h, key| rewire[key] || key}
  !hash1.any? do |key, val| 
    hash2[map[key]] != val
  end
end


hash_a = {:cats_name => 'Bubby', :dog => 'Gizmo'}  
hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'}  
p eql(hash_a, hash_b) #=> false

hash_a = {:cats_name => 'Bubby', :dog => 'Gizmo'}  
hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'}  
p eql(hash_a, hash_b, :cats_name => :cat)  #=> true


hash_a = {:cat => 'Bubby', :dog => 'Gizmo'}  
hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'}  
p eql(hash_a, hash_b) #=> true

hash_a = {:cat => 'Bubby', :dog => 'Gizmo', :fish => "Wanda"}  
hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'}  
p eql(hash_a, hash_b) #=> false

Не слишком долго, и, похоже, работает так, как вы этого хотите :)

person einarmagnus    schedule 21.09.2010

Хех, если вам действительно нужно быстро и элегантно, вот вам:

(a = @hash_a.values; (a & @hash_b.values) == a)

Есть определенные очевидные ограничения...

person DigitalRoss    schedule 21.09.2010
comment
разве это не игнорирует ключи? что если {:cat =› 'Gizmo', :dog =› 'Bubby'} - person Mysrt; 22.09.2010
comment
О, и вам может понадобиться отсортировать результаты, хотя в вашем примере данных это не нужно. - person DigitalRoss; 22.09.2010

Одна из возможностей — сначала переназначить ключи одного хэша, а затем выполнить операцию набора подмножеств:

require 'set'

def remap_keys(hash, key_map)
  hash.inject({}) do |acc, pair|
    key, value = pair
    remapped_key = key_map[key] || key
    acc[remapped_key] = value
    acc
  end
end

def hash_subset?(a, b)
  set_a = Set.new(a)
  set_b = Set.new(b)
  set_a.subset?(set_b)
end

hash_a = {:cats_name => 'Bubby', :dog => 'Gizmo'}  
hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'}  

puts hash_subset?(remap_keys(hash_a, {:cats_name => :cat}), hash_b)

Однако я уверен, что есть более эффективные способы сделать это. Больше чем один способ освежевать :cat, а?!

person Richard Cook    schedule 21.09.2010