【问题标题】:Converting a nested hash into a flat hash将嵌套散列转换为平面散列
【发布时间】:2012-03-27 17:26:33
【问题描述】:

这个问题与this question相反。

给定一个嵌套散列

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

将其转换为平面哈希的最佳方法是什么

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

【问题讨论】:

  • 所以你想要一个键数组来产生一个值?

标签: ruby hash


【解决方案1】:

另一种方式:

def flat_hash(h,f=[],g={})
  return g.update({ f=>h }) unless h.is_a? Hash
  h.each { |k,r| flat_hash(r,f+[k],g) }
  g
end

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

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

【讨论】:

  • 我知道 Ruby 不一定支持开箱即用的 TCO,但如果您没有在此处返回 g,这是否会进行尾调用优化?
  • 我听说过“尾调用优化”这个词,但我不知道它是什么意思。也许计算机科学家可以回答您的问题。
  • 您无法在 IMO 此处优化尾调用,因为您有级联递归。
【解决方案2】:

非常类似于 Adiel Mittmann 的解决方案

def flat_hash(h, k = [])
  new_hash = {}
  h.each_pair do |key, val|
    if val.is_a?(Hash)
      new_hash.merge!(flat_hash(val, k + [key]))
    else
      new_hash[k + [key]] = val
    end
  end
  new_hash
end

编辑:为了优雅而重构。应该几乎一样快。

def flat_hash(hash, k = [])
  return {k => hash} unless hash.is_a?(Hash)
  hash.inject({}){ |h, v| h.merge! flat_hash(v[-1], k + [v[0]]) }
end

【讨论】:

  • 这个跑得最快。谢谢。
  • @sawa:只是对未来的提示:如果您想要一个快速的解决方案,请在下次问题中提及这一点。通常,Python 或 Ruby 等动态语言的主要标准是优雅和简洁。如果您还特别要求性能,您可以获得更合适的答案:)
  • @sawa:为优雅而重构。
  • @Kyle 您之前的代码的一个优点是它可以重新定义为Hash 上的方法。重写的不能。
  • @Niklas 也许是这样,但大多数 Ruby 读者都知道 Sawa 是个速度狂。
【解决方案3】:

我的尝试:

def flatten_hash(h)
  return { [] => h } unless h.is_a?(Hash)
  Hash[h.map { |a,v1| flatten_hash(v1).map { |b,v2| [[a] + b, v2] } }.flatten(1)]
end

抱歉,变量名称不好,必须放在一行中。

【讨论】:

    【解决方案4】:

    这不是试图为您提供最好的方法,但它是一种方法:P

    def flatten(hash)
      return {[] => hash} if !hash.is_a?(Hash)
      map = {}
      hash.each_pair do |key1, value1|
        flatten(value1).each_pair do |key2, value2|
          map[[key1] + key2] = value2
        end
      end
      return map
    end
    

    它适用于您的示例,产生以下结果:

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

    如果有空哈希,它可能不会产生您期望的结果。

    【讨论】:

      【解决方案5】:

      一种功能性方法(请参阅history 了解替代实现):

      def recursive_flatten(hash)
        hash.flat_map do |key, value|
          if value.is_a?(Hash)
            recursive_flatten(value).map { |ks, v| [[key] + ks, v] } 
          else
            [[[key], value]]
          end
        end.to_h
      end
      

      【讨论】:

        【解决方案6】:

        受@cary-swoveland 方式的启发,但在 Hash 类中:

        class Hash
          def deep_flatten(previous_key=[])
            flat_hash = {}
            self.each do |key, value|
              next_key = previous_key+[key]
              flat_hash.update(value.is_a?(Hash) ? value.deep_flatten(next_key) : {next_key=>value})
            end
            return flat_hash
          end
        end
        
        h = { :a => { :b => { :c => 1, :d => 2 }, :e => 3 }, :f => 4 }
        
        h.deep_flatten #=> {[:a, :b, :c]=>1, [:a, :b, :d]=>2, [:a, :e]=>3, [:f]=>4}
        

        【讨论】:

          【解决方案7】:

          使用DeepEnumerable 的声明式解决方案:

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

          h.deep_each.map do |k, v|
            [DeepEnumerable.deep_key_to_array(k), v]
          end.to_h
          

          或者,对于那些喜欢无点风格的人

          h.deep_each.to_h.shallow_map_keys(&DeepEnumerable.method(:deep_key_to_array))
          

          【讨论】:

            【解决方案8】:

            数组支持/可读名称/不更新速度/字符串化结果键

            def flat_hash(input, base = nil, all = {})
              if input.is_a?(Array)
                input = input.each_with_index.to_a.each(&:reverse!)
              end
            
              if input.is_a?(Hash) || input.is_a?(Array)
                input.each do |k, v|
                  flat_hash(v, base ? "#{base}.#{k}" : k, all)
                end
              else
                all[base] = input
              end
            
              all
            end
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-06-24
              • 2013-05-16
              • 2017-07-08
              • 2016-08-10
              • 2018-02-10
              • 2013-03-14
              • 2018-11-07
              相关资源
              最近更新 更多