在 Ruby 中比较两个相似的哈希值

4
我正在使用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] 

我感觉必须有更好的方法,我正在寻找比这更快、更优美的东西。


你的目标是什么?比较代码的简洁性?效率/速度? - GreyCat
4个回答

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 方法。


这可以进一步简化。如果a是b的子集,则(a-b)返回空数组。https://dev59.com/J2865IYBdhLWcg3wR8eh#3897535 - Ankush

2
这是我的做法:
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

不算太长,看起来符合你的要求 :)

1

嘿,如果你真的想要快速而优雅的话,这里有:

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

有一些明显的限制...


这个忽略了键,如果是 {:cat => 'Gizmo', :dog => 'Bubby'} 呢? - Mysrt
哦,你可能需要对结果进行排序,尽管在你的示例数据中不需要。 - DigitalRoss

0
一种可能性是先重新映射一个哈希的键,然后执行一个集合子集操作:
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)

然而,我相信有更有效的方法来做到这一点。多种方式来实现一个目标,是吧?!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接