代码之家  ›  专栏  ›  技术社区  ›  Jagdeep Singh

初始化和更新深度嵌套哈希的更好方法

  •  3
  • Jagdeep Singh  · 技术社区  · 6 年前

    我有一个 Hash 我想在深层次向其中插入一些数据,但是在任何层都可能缺少一个键。所以,我有条件地初始化它,然后在每个级别更新它的值。

    写这篇文章的更好方法是什么,还是一种可以让代码不那么难看的方法?

    data[:foo] ||= {}
    data[:foo][:bar] ||= {}
    data[:foo][:bar][:baz] ||= []
    data[:foo][:bar][:baz] << 99
    
    3 回复  |  直到 6 年前
        1
  •  6
  •   Aleksei Matiushkin    6 年前

    使用 哈希自动激活 :

    data = Hash.new { |h, k| h[k] = h.dup.clear }
    #⇒ {}
    
    # or, credits to @Amadan:
    data = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
    #⇒ {}
    
    data[:foo][:bar][:baz] = 42
    data
    #⇒ {:foo=>{:bar=>{:baz=>42}}}
    

    这里使用的技巧是我们使用 Hash#default_proc 创建嵌套键。

    对于您的案例:

    (data[:foo][:bar][:baz] = []) << 99
    
        2
  •  1
  •   Cary Swoveland    6 年前

    可以使用递归。

    def stuff_it(h, first_key, *rest_keys, val)
      if rest_keys.empty?
        (h[first_key] ||= []) << val
      else
        h[first_key] = stuff_it(h[first_key] ||= {}, *rest_keys, val)
      end
      h
    end   
    
    stuff_it({ a: 1 }, :foo, :bar, :baz, 99)
      #=> {:a=>1, :foo=>{:bar=>{:baz=>[99]}}}
    stuff_it({ a: 1, foo: { b: 2 } }, :foo, :bar, :baz, 99)
      #=> {:a=>1, :foo=>{:b=>2, :bar=>{:baz=>[99]}}}
    stuff_it({ a: 1, foo: { b: 2, bar: { c: 3 } } }, :foo, :bar, :baz, 99)
      #=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[99]}}}
    h = { a: 1, foo: { b: 2, bar: { c: 3, baz: [88] } } }
    stuff_it(h, :foo, :bar, :baz, 99)
      #=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88, 99]}}}
    h # => {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88, 99]}}}
    

    从上一个例子可以看出,这种方法是破坏性的。它可以通过做一个小的改变来实现非破坏性。

    def stuff_it(g, first_key, *rest_keys, val)
      h = g.merge(g)
      if rest_keys.empty?
         h[first_key] = h[first_key] ? h[first_key].dup << val : [val]
      else
        h[first_key] = stuff_it(h[first_key] ||= {}, *rest_keys, val)
      end
      h
    end   
    
    h = { a: 1, foo: { b: 2, bar: { c: 3 } } }
    stuff_it(h, :foo, :bar, :baz, 99)
      #=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[99]}}}
    h #=> { a: 1, foo: { b: 2, bar: { c: 3 } } }
    
    h = { a: 1, foo: { b: 2, bar: { c: 3, baz: [88] } } }
    stuff_it(h, :foo, :bar, :baz, 99)
      #=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88, 99]}}}
    h #=> {:a=>1, :foo=>{:b=>2, :bar=>{:c=>3, :baz=>[88]}}}
    
        3
  •  0
  •   Kris    6 年前

    你也可以做如下的事情:

    class SpecialHash < Hash
      def [](key)
        if has_key?(key)
          super(key)
        else
          self[key] = self.class.new
        end
      end
    end
    
    h = SpecialHash.new
    h[:foo][:bar][:baz] = "Baz"
    h # => {:foo=>{:bar=>{:baz=>"Baz"}}}
    

    它和鸭子的类型完全一样 Hash .

    您可以将相同的代码重新格式化为:

    class SpecialHash < Hash
      def [](key)
        return super if has_key?(key)
        self[key] = self.class.new
      end
    end
    

    甚至

    class SpecialHash < Hash
      def [](key)
        has_key?(key) ? super : self[key] = self.class.new
      end
    end
    
    推荐文章