2

I am looking for a concise way to set the value into a hash deep given a list of key accessors.

path = [:mountain, :river, :tree]
hash = {}
deep_nest(hash, path, 23)
=> { mountain: { river: { tree: 23 } } }

I get it working with the following code

deep = path.inject(hash) do |hash, field|
  if hash.key?(field)
    hash[field]
  else
    hash[field] = {}
  end
end

deep[path.last] = 23

Is there a shorter way? Normally default initialization works neatly on hashes, but this only works for the first, level, maybe this could be done more dynamically.

4

2 回答 2

2

我不确定这是否符合简洁的定义,但解决此问题的一种方法是将值构造为哈希的完整路径,并将其与原始哈希深度合并,允许原始哈希为非空.

path = [:mountain, :river, :tree]
hash = {:mountain=>{:river=>{:tree=>66}, :house=>:red}}
hash.merge!((path + [23]).reverse.reduce { |s,e| { e => s } }) { |k,o,n| o.merge(n) }
# => {:mountain=>{:river=>{:tree=>23}, :house=>:red}}

注意我使用path + [23]而不是path << 23避免修改pathmerge!而不是merge修改hash

阶段的解释(向外工作):

  • (path + [23]).reverse.reduce { |s,e| { e => s } }:创建具有值的路径的哈希
  • hash.merge!( ... ) { |k,o,n| o.merge(n) }: 使用新值合并到旧值的合并策略深度合并哈希

(这个问题与https://www.ruby-forum.com/topic/4417754非常相似,尽管不相同。)

于 2013-10-10T22:17:09.793 回答
2

如果您可以控制哈希初始化:

hash = Hash.new { |h,k| h[k] = Hash.new(&h.default_proc)
path[0..-1].inject(hash) { |hash, key| hash[key] }[path.last] = 23

&h.default_proc每次按下新键时都会分配一个新的 proc 实例。这样就创建了一个无限循环。通过使用default_proc它适用于任意深度,而不是

Hash.new { |h,k| h[k] = Hash.new(self) }

这仅适用于两个级别,因为它引用了相同的 proc 实例。

于 2013-10-10T19:25:22.153 回答