【问题标题】:Converting a hash into a nested hash将散列转换为嵌套散列
【发布时间】:2015-12-19 04:42:36
【问题描述】:

这个问题与this question相反。

给定一个散列,每个键都有一个数组

{
    [:a, :b, :c] => 1,
    [:a, :b, :d] => 2,
    [:a, :e] => 3,
    [:f] => 4,
}

将其转换为嵌套哈希的最佳方法是什么

{
    :a => {
       :b => {:c => 1, :d => 2},
       :e => 3,
    },
    :f => 4,
}

【问题讨论】:

  • 没有什么“递归”的,这是简单的嵌套。
  • @dominikh 特定的散列可能不是递归的,但无论使用何种算法使其成为递归的。但我觉得你说的很有道理。我编辑了它。
  • 到目前为止您尝试过什么?您尝试的解决方案不起作用的具体原因是什么?

标签: ruby hash nested


【解决方案1】:

这是一个迭代解决方案,一个递归解决方案留给读者作为练习:

def convert(h={})
  ret = {}
  h.each do |k,v|
    node = ret
    k[0..-2].each {|x| node[x]||={}; node=node[x]}
    node[k[-1]] = v
  end
  ret
end

convert(your_hash) # => {:f=>4, :a=>{:b=>{:c=>1, :d=>2}, :e=>3}}

【讨论】:

  • 谢谢。您的答案比递归执行要好得多。太棒了。
  • 为什么默认是空哈希?请注意,通过更改为 h.each_with_object({}) do |(k,v),ret| 您可以删除第一行和最后一行(尽管我不确定您回答问题时是否可以使用 each_with_object (v1.9+))。当然,您也可以使用reduce
【解决方案2】:

函数递归算法:

require 'facets'

class Hash
  def nestify
    map_by { |ks, v| [ks.first, [ks.drop(1), v]] }.mash do |key, pairs|
      [key, pairs.first[0].empty? ? pairs.first[1] : Hash[pairs].nestify]
    end
  end
end

p {[:a, :b, :c]=>1, [:a, :b, :d]=>2, [:a, :e]=>3, [:f]=>4}.nestify
# {:a=>{:b=>{:c=>1, :d=>2}, :e=>3}, :f=>4}

【讨论】:

    【解决方案3】:

    已经有一个很好的答案,但是我研究了这个递归解决方案,所以这里是:

    def to_nest(hash)
      {}.tap do |nest|
        hash.each_pair do |key, value|
          nodes = key.dup
          node  = nodes.shift
          if nodes.empty?
            nest[node] = value
          else
            nest[node] ||= {}
            nest[node].merge!({nodes => value})
          end
        end
        nest.each_pair do |key, value|
          nest[key] = to_nest(value) if value.kind_of?(Hash)
        end
      end
    end
    

    【讨论】:

      【解决方案4】:

      另一种方式:

      def convert(h)
        h.each_with_object({}) { |(a,n),f| f.update({ a.first=>(a.size==1 ? n :
          convert({ a[1..-1]=>n })) }) { |_,ov,nv| ov.merge(nv) } }
      end
      

      试试看:

      h = {
          [:a, :b, :c] => 1,
          [:a, :b, :d] => 2,
          [:a, :e] => 3,
          [:f] => 4,
      }
      
      convert(h) #=> {:a=>{:b=>{:d=>2}, :e=>3},
                 #    :f=>4}
      

      【讨论】:

        【解决方案5】:

        对于混合哈希/数组嵌套结构,您可以使用它。 (也针对数组进行了修改)

        def unflatten(h={})
          ret = {}
          h.each do |k,v|
            node = ret
            keys = k.split('.').collect { |x| x.to_i.to_s == x ? x.to_i : x }
            keys.each_cons(2) do |x, next_d|
              if(next_d.is_a? Fixnum)
                node[x] ||= []
                node=node[x]
              else
                node[x] ||={}
                node=node[x]
              end
            end                                                                                                                                                                                                                                                                         
            node[keys[-1]] = v
          end
          ret
        end
        

        如果您使用以下内容进行展平。 (点分隔字符串作为键而不是数组[拆分。如果需要])

        def flatten_hash(hash)
          hash.each_with_object({}) do |(k, v), h|
            if v.is_a? Hash
              flatten_hash(v).map do |h_k, h_v|
                h["#{k}.#{h_k}"] = h_v 
              end 
            elsif v.is_a? Array
              flatten_array(v).map do |h_k,h_v|
                h["#{k}.#{h_k}"] = h_v 
              end 
            else
              h[k] = v 
            end 
          end 
        end
        
        
        def flatten_array(array)
          array.each_with_object({}).with_index do |(v,h),i|
            pp v,h,i
            if v.is_a? Hash
              flatten_hash(v).map do |h_k, h_v|
                h["#{i}.#{h_k}"] = h_v 
              end 
            elsif v.is_a? Array
              flatten_array(v).map do |h_k,h_v|
                h["#{i}.#{h_k}"] = h_v 
              end 
            end 
          end 
        end  
        

        【讨论】:

          【解决方案6】:

          使用DeepEnumerable

          require DeepEnumerable
          
          h = {[:a, :b, :c]=>1, [:a, :b, :d]=>2, [:a, :e]=>3, [:f]=>4}
          
          h.inject({}){|hash, kv| hash.deep_set(*kv)}
          

          【讨论】:

            猜你喜欢
            • 2012-03-27
            • 2013-05-16
            • 2017-07-08
            • 2018-02-10
            • 2013-03-14
            • 2018-11-07
            • 2016-07-18
            • 2011-08-21
            • 1970-01-01
            相关资源
            最近更新 更多