sirion1987
sirion1987

Reputation: 111

How to create a hash from a multidimensional array

I have these params :

x=["room_adults_1", "room_childs_1", "room_adults_2", "room_childs_2"]

And when I run this code:

y = x.map { |x| x.match(/^(room)_(adults|childs)_(\d+)/)}
z = y.map { |x| [x[1],[x[2],[x[3].reverse,""]]]}

I get this array:

 => [["room", ["adults", ["1", ""]]], ["room", ["childs", ["1", ""]]], ["room", ["adults", ["2", ""]]], ["room", ["childs", ["2", ""]]]]

I would like to transform this last result into a Hash. If I use z.to_h (collapse last element), I obtain {"room"=>["childs", ["2", ""]]}. But I would like a Hash like this:

 {
    "room":{
     "adults":{[
      {"1": ""},
      {"2": ""}
     ]},
     "child":{[
      {"1": ""},
      {"2": ""}
     ]} 
    }
  }

How can I do?

Upvotes: 0

Views: 116

Answers (3)

Lukas Baliak
Lukas Baliak

Reputation: 2869

In this case I prefer each_with_object:

x = ["room_adults_1", "room_childs_1", "room_adults_2", "room_childs_2"]

export = x.each_with_object(Hash.new { |k, v| k[v] = Hash.new { |k, v| k[v] = [] } }) do |string, exp|
  room, type, id = string.split("_")
  exp[room][type] << {id => ""}
end

p export
# => {"room"=>{"adults"=>[{"1"=>""}, {"2"=>""}], "childs"=>[{"1"=>""}, {"2"=>""}]}}

Upvotes: 3

Cary Swoveland
Cary Swoveland

Reputation: 110675

I assume the desired result is

{:room=>{:adults=>[{:"1"=>""}, {:"2"=>""}], :childs=>[{:"1"=>""}, {:"2"=>""}]}} 

as the construct given in the question is not a valid Ruby object, let alone a hash.

arr = ["room_adults_1", "room_childs_1", "room_adults_2", "room_childs_2"]

h = arr.map { |s| s.split('_')[1,2].map(&:to_sym) }.group_by(&:first)
  #=> {:adults=>[[:adults, :"1"], [:adults, :"2"]],
  #    :childs=>[[:childs, :"1"], [:childs, :"2"]]}

{ room: h.merge(h) { |k,a,_| a.map { |_,b| { b=>"" } } } }
  #=> {:room=>{:adults=>[{:"1"=>""}, {:"2"=>""}], :childs=>[{:"1"=>""}, {:"2"=>""}]}} 

If the keys are to be strings, not symbols, remove .map(&:to_sym).

Upvotes: 2

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

x.map { |e| e.split '_' }
 .group_by(&:shift) # room
 .map { |k, v| [k, v.group_by(&:shift) # adults/children
                    .map { |k, v| [k, v.map { |e, *| {e => ""} } ] }
                    .to_h ] }
 .to_h

#⇒ {"room"=>{"adults"=>[{"1"=>""}, {"2"=>""}],
#            "childs"=>[{"1"=>""}, {"2"=>""}]}}

Upvotes: 4

Related Questions