【问题标题】:Recursively setting hash keys from an array of keys从键数组递归设置哈希键
【发布时间】:2017-09-27 21:19:08
【问题描述】:

我想要一个函数,它可以采用 [: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,那就是我正在寻找的。​​p>

【问题讨论】:

  • 你为什么想要这个? o.0'
  • 我正在将父->子关系从数组映射到哈希。
  • 第一个参数是什么意思?我不太明白你的目的是什么
  • '寻找最短、最优雅的答案" - 基于意见的问题。"built-in or one-liner" – 抱歉,我通常不会评论这些东西,但你在 2 句话中触发了我 4 次......
  • @naomik 按照这些标准,这应该在 codegolf.stackexchange.com

标签: arrays ruby ruby-hash


【解决方案1】:

代码

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}

观察:d 的值在j 的计算中被覆盖。另请注意:

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 模块的方法可用于创建散列的深拷贝,因此原始散列不会被改变。

【讨论】:

    【解决方案2】:

    用递归解决:

    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
    

    【讨论】:

      【解决方案3】:
      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
      

      【讨论】:

        猜你喜欢
        • 2015-03-28
        • 2016-07-11
        • 1970-01-01
        • 1970-01-01
        • 2019-04-30
        • 2012-03-27
        • 2012-05-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多