使用Ruby/Rails将嵌套哈希展平为单个哈希

yer*_*ips 17 ruby recursion ruby-on-rails

我想在.flatten不同深度的哈希中"扁平化"(不是在经典意义上),如下所示:

{
  :foo => "bar",
  :hello => {
    :world => "Hello World",
    :bro => "What's up dude?",
  },
  :a => {
    :b => {
      :c => "d"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

下到一个单一级别的哈希,并且所有嵌套的键合并为一个字符串,因此它将成为:

{
  :foo => "bar",
  :"hello.world" => "Hello World",
  :"hello.bro" => "What's up dude?",
  :"a.b.c" => "d"
}
Run Code Online (Sandbox Code Playgroud)

但我想不出一个好办法.它有点像deep_Rails添加到Hashes 的辅助函数,但不完全相同.我知道递归将是这里的方式,但我从未在Ruby中编写过递归函数.

Uri*_*ssi 40

你可以这样做:

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}".to_sym] = h_v
      end
    else 
      h[k] = v
    end
   end
end

flatten_hash(:foo => "bar",
  :hello => {
    :world => "Hello World",
    :bro => "What's up dude?",
  },
  :a => {
    :b => {
      :c => "d"
    }
  })
# => {:foo=>"bar", 
# =>  :"hello.world"=>"Hello World", 
# =>  :"hello.bro"=>"What's up dude?", 
# =>  :"a.b.c"=>"d"} 
Run Code Online (Sandbox Code Playgroud)


Osc*_*car 8

这里投票最高的答案不会完全展平对象,它不会展平数组。我在下面更正了这一点并提供了比较:

x = { x: 0, y: { x: 1 }, z: [ { y: 0, x: 2 }, 4 ] }

def top_voter_function ( hash )
  hash.each_with_object( {} ) do |( k, v ), h|
    if v.is_a? Hash
      top_voter_function( v ).map do |h_k, h_v|
        h[ "#{k}.#{h_k}".to_sym ] = h_v
      end
    else
      h[k] = v
    end
  end
end

def better_function ( a_el, a_k = nil )
  result = {}

  a_el = a_el.as_json

  a_el.map do |k, v|
    k = "#{a_k}.#{k}" if a_k.present?
    result.merge!( [Hash, Array].include?( v.class ) ? better_function( v, k ) : ( { k => v } ) )
  end if a_el.is_a?( Hash )

  a_el.uniq.each_with_index do |o, i|
    i = "#{a_k}.#{i}" if a_k.present?
    result.merge!( [Hash, Array].include?( o.class ) ? better_function( o, i ) : ( { i => o } ) )
  end if a_el.is_a?( Array )

  result
end

top_voter_function( x ) #=> {:x=>0, :"y.x"=>1, :z=>[{:y=>0, :x=>2}, 4]}
better_function( x ) #=> {"x"=>0, "y.x"=>1, "z.0.y"=>0, "z.0.x"=>2, "z.1"=>4} 
Run Code Online (Sandbox Code Playgroud)

我意识到这个问题有点老了,我在网上查找上面的代码的比较,这就是我发现的。当与 Mixpanel 等分析服务的事件一起使用时,它的效果非常好。

  • 感谢您提交,我发现这非常有用! (3认同)

小智 5

因为我很喜欢Enumerable#reduce和讨厌台词:

def flatten_hash(param, prefix=nil)
  param.each_pair.reduce({}) do |a, (k, v)|
    v.is_a?(Hash) ? a.merge(flatten_hash(v, "#{prefix}#{k}.")) : a.merge("#{prefix}#{k}".to_sym => v)
  end
end
Run Code Online (Sandbox Code Playgroud)
irb(main):118:0> flatten_hash(hash)
=> {:foo=>"bar", :"hello.world"=>"Hello World", :"hello.bro"=>"What's up dude?", :"a.b.c"=>"d"}
Run Code Online (Sandbox Code Playgroud)