【问题标题】:Comparing ruby hashes [duplicate]比较红宝石哈希[重复]
【发布时间】:2009-11-19 21:37:15
【问题描述】:

可能重复:
How do I compare two hashes?

我有两个 ruby​​ 哈希值(本质上是模型),我试图找出它们之间的差异,一个是对象的旧实例,另一个对象具有分配给某些属性的新值。我正在尝试确定哪些键已更改,但哈希中似乎没有为此内置任何内容。我能想到一些蛮力的解决方案,但想知道是否有一个优雅的解决方案。

理想情况下,我需要能够像这样获取两个哈希:

element1 = {:name => "Original", :description => "The original one!"}
element2 = {:name => "Original", :description => "The new one!"}

并且能够比较/区分它们并得到这样的结果:

{:description => "The new one!"}

现在我真正能想到的只是遍历一个哈希中的键并将该键的值与第二个哈希中的相应键进行比较,但这似乎太暴力了。

有什么想法吗?非常感谢!

【问题讨论】:

    标签: ruby hash comparison


    【解决方案1】:

    这里是来自 colin's 的稍作修改的版本。

    class Hash
      def diff(other)
        (self.keys + other.keys).uniq.inject({}) do |memo, key|
          unless self[key] == other[key]
            if self[key].kind_of?(Hash) &&  other[key].kind_of?(Hash)
              memo[key] = self[key].diff(other[key])
            else
              memo[key] = [self[key], other[key]] 
            end
          end
          memo
        end
      end
    end
    

    它递归到哈希中以获得更高效的左右

    {a: {c: 1, b: 2}, b: 2}.diff({a: {c: 2, b: 2}})
    

    返回

    {:a=>{:c=>[1, 2]}, :b=>[2, nil]}
    

    而不是

    {:a=>[{:c=>1, :b=>2}, {:c=>2, :b=>2}], :b=>[2, nil]}
    

    好主意科林

    这里是如何将差异应用于原始哈希

      def apply_diff!(changes, direction = :right)
        path = [[self, changes]]
        pos, local_changes = path.pop
        while local_changes
          local_changes.each_pair {|key, change|
            if change.kind_of?(Array)
              pos[key] = (direction == :right) ? change[1] : change[0]
            else
              path.push([pos[key], change])
            end
          }
          pos, local_changes = path.pop
        end
        self
      end
      def apply_diff(changes, direction = :right)
        cloned = self.clone
        path = [[cloned, changes]]
        pos, local_changes = path.pop
        while local_changes
          local_changes.each_pair {|key, change|
            if change.kind_of?(Array)
              pos[key] = (direction == :right) ? change[1] : change[0]
            else
              pos[key] = pos[key].clone
              path.push([pos[key], change])
            end
          }
          pos, local_changes = path.pop
        end
        cloned
      end 
    

    所以让左边看起来像你跑步的右边

    {a: {c: 1, b: 2}, b: 2}.apply_diff({:a=>{:c=>[1, 2]}, :b=>[2, nil]})
    

    得到

    {a: {c: 2, b: 2}, b: nil}
    

    为了准确起见,我们必须走得更远,并记录 nil 和 no key 之间的区别
    并且只提供添加和删除来缩短长数组也会很好

    【讨论】:

      【解决方案2】:

      编辑:

      我不断返回此代码以在我参与的项目中使用它。这是最新的,它对深度嵌套的结构很有用,并且基于上面 Pete 的代码。我通常将它放在 config/initializers/core_ext.rb 中(在 Rails 项目中):

      class Hash
        def deep_diff(other)
          (self.keys + other.keys).uniq.inject({}) do |memo, key|
            left = self[key]
            right = other[key]
      
            next memo if left == right
      
            if left.respond_to?(:deep_diff) && right.respond_to?(:deep_diff)
              memo[key] = left.deep_diff(right)
            else
              memo[key] = [left, right]
            end
      
            memo
          end
        end
      end
      
      class Array
        def deep_diff(array)
          largest = [self.count, array.count].max
          memo = {}
      
          0.upto(largest - 1) do |index|
            left = self[index]
            right = array[index]
      
            next if left == right
      
            if left.respond_to?(:deep_diff) && right.respond_to?(:deep_diff)
              memo[index] = left.deep_diff(right)
            else
              memo[index] = [left, right]
            end
          end
      
          memo
        end
      end
      

      这是一个小演示:

      > {a: [{b: "c", d: "e"}, {b: "c", f: "g"}]}.deep_diff({a: [{b: "c", d: "e"}, {b: "d", f: "g"}]})
      => {:a=>{1=>{:b=>["c", "d"]}}}
      

      较早的回复:

      我发现 Rails 的 Hash diff 方法实际上并不能告诉我左侧和右侧是什么(这更有用)。有一个名为“Riff”的插件,后来消失了,它可以让你区分两个 ActiveRecord 对象。本质上:

      class Hash
        def diff(other)
          self.keys.inject({}) do |memo, key|
            unless self[key] == other[key]
              memo[key] = [self[key], other[key]] 
            end
            memo
          end
        end
      end
      

      【讨论】:

      • 出于我的目的,我并不特别关心,因为我真的只需要知道哪些字段发生了变化。如果我使用 AR,这不会是一个问题,但一切都通过数据层抽象到 CouchDB,所以我发现自己需要重新发明轮子,可以说是为了某些功能。不过感谢您的建议。
      • 这当然与您的“蛮力”评论相对应,但我觉得它很有用,而且没有那么糟糕或不雅。
      • 此方法不会注意到 other 哈希中的其他键,也无法从值为 nil 的值中判断键缺失,以改进版本检查 stackoverflow.com/a/19184270/54247
      【解决方案3】:

      如果您只关心 element2 中的独特之处,您可以这样做:

      element2.to_a - element1.to_a
      

      【讨论】:

      • 如果散列包含其他散列似乎不起作用
      • 是的,因为“相同”的哈希算不上相等...
      • 考虑使用#to_set 而不是#to_a,因为哈希键已经是唯一的
      猜你喜欢
      • 1970-01-01
      • 2010-12-18
      • 2012-12-19
      • 1970-01-01
      • 2011-09-23
      • 2014-11-01
      • 1970-01-01
      • 2012-10-17
      • 1970-01-01
      相关资源
      最近更新 更多