给定:
h = {foo: {bar: 1}}
如何在不知道有多少个键的情况下设置
bar
?例如:
keys = [:foo, :bar]
h[keys[0]][keys[1]] = :ok
但是,如果键可以是任意长度,并且 h 的深度也是任意的呢?
dig
方法来实现如下功能:h.dig(*keys[0..-2])[keys.last] = :ok
dig
方法通过哈希表中的路径查找并返回结果。但是dig
不会复制所找到的内容,因此您将得到与h
中相同的引用。keys[0..-2]
获取除keys
中最后一个元素外的所有元素,因此h.dig(*keys[0..-2])
从h
内部获取{bar: 1}
哈希表,然后可以使用简单的赋值在原地修改它。*head, tail = keys
h.dig(*head)[tail] = :ok
如果对您来说比 [0..-2]
更清晰,可以使用该方式。
如果您没有 dig
,则可以进行以下操作:
*head, tail = keys
head.inject(h) { |m, k| m[k] }[tail] = :ok
keys
指定的路径是否存在,则需要添加一些nil
检查,并决定如何处理未指定到h
的keys
。path = keys.dup; last_key = path.pop
,但是 *head, tail = keys
更好。 - B Sevendig
的解决方案假定 keys.size >= 2
。 - ribamardig
方法,所以在这里似乎只能使用 inject
方法 =/!我甚至尝试过使用 dig
方法来访问值,但是对于设置操作,我得到了不同的 ActionController::Parameters 对象的 object_ids,或者使用某些语句设置值的次数很奇怪等问题,因此我可能会将 inject
代码转换为 Hash#bury
! - Pysishead.inject(h) { |m, k| m[k] ||= {} }[tail] = :ok
。 - Alex Davish = {foo: {bar: 1}}
keys = [:foo, :bar]
h2 = {
foo: {
bar: {
baz: {
setting: :bad
}
}
}
}
keys2 = [:foo, :bar, :baz, :setting]
def set_nested_value(hash, keys, new_val)
if keys.length == 1
hash[keys.first] = new_val
return
end
keys.each_with_index do |key, i|
if hash[key]
set_nested_value(hash[key], keys[i+1..-1], new_val)
end
end
end
set_nested_value(h, keys, :ok)
p h
=> {:foo=>{:bar=>:ok}}
set_nested_value(h2, keys2, :good)
p h2
=> {:foo=>{:bar=>{:baz=>{:setting=>:good}}}}