将嵌套哈希转换为平面哈希

这个问题是这个问题的反面。

给定一个嵌套的哈希像

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

什么是最好的方式将它转换成一个平坦的哈希像

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

其他方式:

 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} 

与Adiel Mittmann的解决scheme非常相似

 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 

我的尝试:

 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 

对不起,不好的variables名称,必须在一行。

这不是试图给你最好的办法,但它是一种方式: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} 

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

function性的方法(请参阅其他实现的历史logging ):

 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 

灵感来自@ cary-swoveland的方式,但在哈希class:

 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} 

使用DeepEnumerable的声明式解决scheme:

 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)) 

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

 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