Сравнение двух похожих хэшей в ruby - PullRequest
4 голосов
/ 21 сентября 2010

Я использую ruby ​​1.8.7, и мне нужно сравнить два имеющихся у меня хэша, которые по сути являются атрибутами модели.Хэш A меньше, чем Hash B, а Hash 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] 

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

Ответы [ 4 ]

3 голосов
/ 22 сентября 2010

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

@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.

1 голос
/ 21 сентября 2010

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

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

Существуют определенные очевидные ограничения ...

1 голос
/ 22 сентября 2010

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

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

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

0 голосов
/ 21 сентября 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, а?!

...