从一个键数组递归设置哈希键

3
我希望有一个函数,能够接收像 [:a, :b, :c] 这样的数组,并递归设置哈希键,随着操作的进行创建所需要的内容。
hash = {}

hash_setter(hash, [:a, :b, :c], 'value') 
hash #=> {:a => {:b => {:c => 'value' } } }

hash_setter(hash, [:a, :b, :h], 'value2') 
hash #=> {:a => {:b => {:c => 'value', :h => 'value2' } } }

我知道Ruby 2.3的dig可以用于这种方式的获取,但那还不足以得到答案。如果有一个等效于dig的setter就是我所寻找的。


2
你为什么想要这个?o.0' - Alexander
第一个参数的作用是什么?我不太明白你的目的是什么。 - Alexander
@Alexander 第一个参数是你正在修改的哈希。 - Ben G
2
“寻找最短、最优雅的答案” - 这是一个基于观点的问题。 “内置还是一行代码” - 抱歉,我通常不会评论这些东西,但你在两个句子中引发了我的情绪。” - Mulan
2
按照这些标准,这应该在codegolf.stackexchange.com上。 - Alexander
显示剩余5条评论
3个回答

0

代码

def nested_hash(keys, v, h={})
  return subhash(keys, v) if h.empty?
  return h.merge(subhash(keys, v)) if keys.size == 1
  keys[0..-2].reduce(h) { |g,k| g[k] }.update(keys[-1]=>v)
  h
end

def subhash(keys, v)
  *first_keys, last_key = keys
  h = { last_key=>v }
  return h if first_keys.empty?
  first_keys.reverse_each.reduce(h) { |g,k| g = { k=>g } }
end

示例

h = nested_hash([:a, :b, :c], 14)    #=> {:a=>{:b=>{:c=>14}}}
i = nested_hash([:a, :b, :d], 25, h) #=> {:a=>{:b=>{:c=>14, :d=>25}}}
j = nested_hash([:a, :b, :d], 99, i) #=> {:a=>{:b=>{:c=>14, :d=>99}}}
k = nested_hash([:a, :e], 104, j)    #=> {:a=>{:b=>{:c=>14, :d=>99}, :e=>104}}
    nested_hash([:f], 222, k)        #=> {:a=>{:b=>{:c=>14, :d=>99}, :e=>104}, :f=>222}

观察到在计算j时,:d的值被覆盖。还要注意:
subhash([:a, :b, :c], 12)
  #=> {:a=>{:b=>{:c=>12}}}

这会改变哈希表h的值。如果不想改变,可以插入以下代码:

  f = Marshal.load(Marshal.dump(h))

return subhash(keys, v) if h.empty? 代码行后,并将随后的 h 引用更改为 f。可以使用 Marshal 模块中的方法创建哈希的 深拷贝,以便原始哈希不会被修改。

0

使用递归解决:

def hash_setter(hash, key_arr, val)
  key = key_arr.shift
  hash[key] = {} unless hash[key].is_a?(Hash)
  key_arr.length > 0 ? hash_setter(hash[key], key_arr, val) : hash[key] = val
end

-2
def set_value_for_keypath(initial, keypath, value)
    temp = initial

    for key in keypath.first(keypath.count - 1)
        temp = (temp[key] ||= {})
    end

    temp[keypath.last] = value

    return initial
end

initial = {:a => {:b => {:c => 'value' } } }

set_value_for_keypath(initial, [:a, :b, :h], 'value2')

initial

或者如果您更喜欢一些更难读的东西:

def set_value_for_keypath(initial, keypath, value)
    keypath.first(keypath.count - 1).reduce(initial) { |hash, key| hash[key] ||= {} }[keypath.last] = value
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接