【问题标题】:How can I merge two hashes without overwritten duplicate keys in Ruby?如何在不覆盖 Ruby 中的重复键的情况下合并两个哈希?
【发布时间】:2009-12-30 15:37:20
【问题描述】:

有没有一种简单或优雅的方法来合并两个哈希而不覆盖重复键?

也就是说,如果密钥存在于原始哈希中,我不想更改它的值。

【问题讨论】:

  • 你真的是指数组(例如:['a', 'b', 'c'])还是哈希(例如:{'a' => 1, 'b' => 2, 'c' => 3})?
  • 抱歉,我说的是哈希 :)

标签: ruby


【解决方案1】:

如果您有两个哈希值,optionsdefaults,并且您想在不覆盖现有键的情况下将 defaults 合并到 options,您真正想要做的是相反的操作:将 options 合并到 @ 987654327@:

options = defaults.merge(options)

或者,如果您使用的是 Rails,您可以这样做:

options.reverse_merge!(defaults)

【讨论】:

  • 完全同意,非常感谢 reverse_merge!方法不知道它:)
  • 为什么这里需要括号?你不能只做它出现的 default.merge 选项。
  • 由于 Rails 5.1 中的安全问题,他们正在弃用 reverse_merge!
  • @Mirv-Matt - 我没有看到折旧通知。 apidock.com/rails/v6.0.0/Hash/reverse_merge%21
【解决方案2】:

标准 Ruby 库中有一种方法可以合并散列而不覆盖现有值或重新分配散列。

important_hash.merge!(defaults) { |key, important, default| important }

【讨论】:

    【解决方案3】:

    如果您的问题是原始哈希值和第二个哈希值都可能具有重复的键,并且您不想在任一方向上覆盖,您可能需要进行简单的手动合并,并进行某种冲突检查和处理:

    hash2.each_key do |key|
      if ( hash1.has_key?(key) )
           hash1[ "hash2-originated-#{key}" ] = hash2[key]
      else
           hash1[key]=hash2[key]
      end
    end
    

    显然,这是非常基本的,并假设 hash1 没有任何称为“hash2-originated-whatever”的键 - 你最好只向键添加一个数字,使其变为 key1、key2 等等,直到你找到了一个不在 hash1 中的。另外,我已经有几个月没有做任何 ruby​​ 了,所以这可能在语法上不正确,但你应该能够理解要点。

    或者将键的值重新定义为一个数组,以便 hash1[key] 返回 hash1 的原始值和 hash2 的值。取决于你希望你的结果是什么。

    【讨论】:

    • 如果不保留两个键,而是将同一个键的值相加怎么样?
    • @TomK.C.Chiu 这在很大程度上取决于我们无法从问题中判断的情况 - 如果 hash1 中的值是字符串而 hash2 是整数怎么办?在某些情况下,这可能是一个可行的选择,但更多时候它会导致问题 - 使用列表作为值的建议非常干净地解决了这个问题。
    【解决方案4】:

    在这里您可以通过reverse_merge 合并您的 2 哈希

    order = {
     id: 33987,
     platform: 'web'
    }
    
    user = {
      name: 'Jhon Doe',
      email: 'jhon.doe@gmail.com' 
    }
    newHash = oder.reverse_merge!(user)
    render json: { data: newHash, status: 200 }
    

    【讨论】:

      【解决方案5】:

      如果您想合并两个哈希 optionsdefaults 而不覆盖目标哈希,您可以使用 select 检查目标哈希中是否已经存在密钥。这是没有 Rails 的纯 Ruby 解决方案:

      options  = { "a" => 100, "b" => 200 }
      defaults = { "b" => 254, "c" => 300 }
      options.merge!(defaults.select{ |k,_| not options.has_key? k })
      
      # output
      # => {"a"=>100, "b"=>200, "c"=>300}
      

      或者如果密钥存在,但包含 nil 并且您想要覆盖它:

      options.merge!(defaults.select{ |k,_| options[k].nil? })
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-18
        • 2015-11-22
        • 2016-11-15
        • 1970-01-01
        • 1970-01-01
        • 2021-12-23
        相关资源
        最近更新 更多