[英]Flattening nested hash to a single hash with Ruby/Rails
I want to "flatten" (not in the classical sense of .flatten
) down a hash with varying levels of depth, like this:我想“展平”(不是经典意义上的
.flatten
)具有不同深度级别的哈希,如下所示:
{
:foo => "bar",
:hello => {
:world => "Hello World",
:bro => "What's up dude?",
},
:a => {
:b => {
:c => "d"
}
}
}
down into a hash with one single level, and all the nested keys merged into one string, so it would become this:向下变成一个单一级别的散列,所有嵌套的键合并成一个字符串,所以它会变成这样:
{
:foo => "bar",
:"hello.world" => "Hello World",
:"hello.bro" => "What's up dude?",
:"a.b.c" => "d"
}
but I can't think of a good way to do it.但我想不出一个好的方法来做到这一点。 It's a bit like the
deep_
helper functions that Rails adds to Hashes, but not quite the same.有点像 Rails 给
deep_
添加的deep_
辅助函数,但又不太一样。 I know recursion would be the way to go here, but I've never written a recursive function in Ruby.我知道递归将是这里的方法,但我从未在 Ruby 中编写过递归函数。
You could do this: 你可以这样做:
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"}
Because I love Enumerable#reduce
and hate lines apparently: 因为我很喜欢
Enumerable#reduce
and hate lines:
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
irb(main):118:0> flatten_hash(hash)
=> {:foo=>"bar", :"hello.world"=>"Hello World", :"hello.bro"=>"What's up dude?", :"a.b.c"=>"d"}
The top voted answer here will not flatten the object all the way, it does not flatten arrays. 这里最高投票的答案不会一直压扁对象,也不会使数组变平。 I've corrected this below and have offered a comparison:
我在下面更正了这个并提供了一个比较:
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}
I appreciate that this question is a little old, I went looking online for a comparison of my code above and this is what I found. 我很欣赏这个问题有点老了,我在网上寻找上面代码的比较,这就是我发现的。 It works really well when used with events for an analytics service like Mixpanel.
当与Mixpanel等分析服务的事件一起使用时,它的效果非常好。
In my case I was working with the Parameters class so none of the above solutions worked for me. 在我的情况下,我正在使用Parameters类,所以上述解决方案都不适合我。 What I did to resolve the problem was to create the following function:
我为解决问题所做的是创建以下功能:
def flatten_params(param, extracted = {})
param.each do |key, value|
if value.is_a? ActionController::Parameters
flatten_params(value, extracted)
else
extracted.merge!("#{key}": value)
end
end
extracted
end
Then you can use it like flatten_parameters = flatten_params(params)
. 然后你可以像
flatten_parameters = flatten_params(params)
一样使用它。 Hope this helps. 希望这可以帮助。
Or if you want a monkey-patched version or Uri's answer to go your_hash.flatten_to_root
: 或者,如果你想要一个猴子修补版本或Uri的答案去你的
your_hash.flatten_to_root
:
class Hash
def flatten_to_root
self.each_with_object({}) do |(k, v), h|
if v.is_a? Hash
v.flatten_to_root.map do |h_k, h_v|
h["#{k}.#{h_k}".to_sym] = h_v
end
else
h[k] = v
end
end
end
end
Just in case, that you want to keep their parent以防万一,你想保留他们的父母
def flatten_hash(param)
param.each_pair.reduce({}) do |a, (k, v)|
v.is_a?(Hash) ? a.merge({ k.to_sym => '' }, flatten_hash(v)) : a.merge(k.to_sym => v)
end
end
hash = {:foo=>"bar", :hello=>{:world=>"Hello World", :bro=>"What's up dude?"}, :a=>{:b=>{:c=>"d"}}}
flatten_hash(hash)
# {:foo=>"bar", :hello=>"", :world=>"Hello World", :bro=>"What's up dude?", :a=>"", :b=>"", :c=>"d"}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.