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

sbs*_*sbs 17 ruby hash dig safe-navigation-operator ruby-2.3

h = {
  data: {
    user: {
      value: "John Doe" 
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

h[:data][:user][:value] = "Bob"
Run Code Online (Sandbox Code Playgroud)

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

就像是

h.dig(:data, :user, :value) = "Bob"
Run Code Online (Sandbox Code Playgroud)

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

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

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

但有更好的方法吗?

Jor*_*ing 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" } } }
Run Code Online (Sandbox Code Playgroud)

  • 您还可以为现有哈希设置默认proc:`hsh.default_proc = proc {| h,k | h [k] = Hash.new(&h.default_proc)}` (6认同)

Nie*_*ian 6

并以@rellampec 的回答为基础,这些回答不会引发错误:

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

obj = {d: 'hey'}
dig_set(obj, [:a, :b, :c], 'val')
obj #=> {d: 'hey', a: {b: {c: 'val'}}} 
Run Code Online (Sandbox Code Playgroud)