When using Ruby, you may sometimes need to flatten a nested hash like:
{
  :foo => "bar",
  :hello => {
    :world => "Hello World",
    :bro => "What's up dude?",
  },
  :a => { :b => { :c => "d" } }
}
to a single hash like:
{
  :foo => "bar",
  :"hello.world" => "Hello World",
  :"hello.bro" => "What's up dude?",
  :"a.b.c" => "d"
}
Today, I introduce a way to achieve it referring to a post in Stack Overflow.
Let’s implement
def flatten_hash_from(hash)
  hash.each_with_object({}) do |(key, value), memo|
    next flatten_hash_from(value).each do |k, v|
      memo["#{key}.#{k}".intern] = v
    end if value.is_a? Hash
    memo[key] = value
  end
end
Using Enumberable#each_with_object recursively helps us achieve it.
Let’s deep dive into how it works.
How it works
First,
key = :foo,value = "bar"is given. the value is not a hash, somemobecomes{ :foo => "bar" }and the process continues.key = :hello,value = { :world => "Hello World", :bro => "What's up dude?" }is given. The value is a hash, soflatten_hash_fromis invoked again with the value.After STEP 2,
memois{}again, which is different from the one in STEP 1. Then,key = :world,value = "Hello World"is given, which is not a hash, somemobecomes{ :world => "Hello World" }. Similarly,key = :brois given next, thenmemobecomes{ :world => "Hello World", :bro => "What's up dude?" }and the result is returned to STEP 2.From STEP 3, the process goes back to
eachpart withkey = :hello. Bymemo["#{key}.#{k}".intern] = v,memobecomes{ :foo => "bar", :"hello.world" => "Hello World", :"hello.bro" => "What's up dude?" }.Next,
key = :a,value = { :b => { :c => "d" } }is given. The value is a hash then,flatten_hash_fromis invoked recursively like STEP2.flatten_hash_fromis invoked withkey = :b,value = { :c => "d" }.{ :c => "d" }is returned to STEP 6.{ :"b.c" => "d" }is returned to STEP 5.From STEP 5, the process goes back to
eachpart withkey = :aand{ :"a.b.c" => "d" }is added tomemo. It turns outmemois{ :foo => "bar", :"hello.world" => "Hello World", :"hello.bro" => "What's up dude?", :"a.b.c" => "d" }, then the process completed.
    
Top comments (0)