Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

{   :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. I know recursion would be the way to go here, but I've never written a recursive function in Ruby.

like image 799
yerforkferchips Avatar asked May 07 '14 14:05

yerforkferchips


People also ask

How do you merge hashes in Ruby?

We can merge two hashes using the merge() method. When using the merge() method: Each new entry is added to the end. Each duplicate-key entry's value overwrites the previous value.

What does the flatten method do in Ruby?

The flatten() is an inbuilt method in Ruby returns a new set that is a copy of the set, flattening each containing set recursively. Parameters: The function does not takes any parameter. Return Value: It returns a boolean value. It returns true if the set is empty or it returns false.

What is a nested hash?

Nested hashes allow us to further group, or associate, the data we are working with. They help us to deal with situations in which a category or piece of data is associated not just to one discrete value, but to a collection of values.


Video Answer


2 Answers

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"}  
like image 51
Uri Agassi Avatar answered Sep 18 '22 19:09

Uri Agassi


Because I love Enumerable#reduce and hate lines apparently:

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"} 
like image 22
ChudMunson Avatar answered Sep 21 '22 19:09

ChudMunson