使用Hash #dig或Lonely运算符(&。)安全地为嵌套哈希值赋值

问题描述 投票:16回答:3
h = {
  data: {
    user: {
      value: "John Doe" 
    }
  }
}

要为嵌套哈希值赋值,我们可以使用

h[:data][:user][:value] = "Bob"

但是,如果缺少中间的任何部分,则会导致错误。

就像是

h.dig(:data, :user, :value) = "Bob"

不起作用,因为还没有Hash#dig=可用。

为了安全地分配价值,我们可以做到

h.dig(:data, :user)&.[]=(:value, "Bob")    # or equivalently
h.dig(:data, :user)&.store(:value, "Bob")

但有更好的方法吗?

ruby hash dig ruby-2.3 safe-navigation-operator
3个回答
13
投票

它并非没有它的警告(如果你从其他地方收到哈希值,它就不起作用),但一个常见的解决方案是:

hash = Hash.new {|h,k| h[k] = h.class.new(&h.default_proc) }

hash[:data][:user][:value] = "Bob"
p hash
# => { :data => { :user => { :value => "Bob" } } }

0
投票

我找到了一个简单的解决方案来设置嵌套哈希的值,即使父键缺失,即使哈希已经存在。鉴于:

x = { gojira: { guitar: { joe: 'charvel' } } }

假设您想要包含马里奥的鼓来导致:

x = { gojira: { guitar: { joe: 'charvel' }, drum: { mario: 'tama' } } }

我结束了猴子修补哈希:

class Hash

    # ensures nested hash from keys, and sets final key to value
    # keys: Array of Symbol|String
    # value: any
    def nested_set(keys, value)
      raise "DEBUG: nested_set keys must be an Array" unless keys.is_a?(Array)

      final_key = keys.pop
      return unless valid_key?(final_key)
      position = self
      for key in keys
        return unless valid_key?(key)
        position[key] = {} unless position[key].is_a?(Hash)
        position = position[key]
      end
      position[final_key] = value
    end

    private

      # returns true if key is valid
      def valid_key?(key)
        return true if key.is_a?(Symbol) || key.is_a?(String)
        raise "DEBUG: nested_set invalid key: #{key} (#{key.class})"
      end
end

用法:

x.nested_set([:instrument, :drum, :mario], 'tama')

用于您的示例:

h.nested_set([:data, :user, :value], 'Bob')

我错过了任何警告?在不牺牲可读性的情况下编写代码的更好方法是什么?


0
投票

有趣的一个:

def dig_set(obj, keys, value)
  if keys.length == 1
    obj[keys.first] = value
  else
    dig_set(obj[keys.first], keys.slice(1..-1), value)
  end
end

如果没有[][]=方法,将提出异常。

© www.soinside.com 2019 - 2024. All rights reserved.