user3558311
user3558311

Reputation: 13

Creating multi-dimensional array of hashes

I want to loop through a multi-dimensional array:

array = [[1,2,3,4,5], [6,7,8,9,10]] 

and create a hash with keys from a another array:

keyValues = "one","two","three","four","five"

I have the following code to do this:

hash = Hash.new
multiArray = Array.new
array.each do |values|
  keyValues.each do |key|
    i = keyValues.index(key)
    hash[key] = values[i]
  end
  puts hash
  multiArray << hash     
end
puts multiArray

The puts hash outputs:

{"one"=>1, "two"=>2, "three"=>3, "four"=>4, "five"=>5}
{"one"=>6, "two"=>7, "three"=>8, "four"=>9, "five"=>10}

and the final multiArray is:

{"one"=>6, "two"=>7, "three"=>8, "four"=>9, "five"=>10}
{"one"=>6, "two"=>7, "three"=>8, "four"=>9, "five"=>10}

I can't figure out why I'm not getting:

{"one"=>1, "two"=>2, "three"=>3, "four"=>4, "five"=>5}

for the final multiArray.

Upvotes: 0

Views: 175

Answers (3)

Boris Stitnicky
Boris Stitnicky

Reputation: 12578

First, install y_support gem (gem install y_support). It defines Array#>> operator useful for constructing hashes:

require 'y_support/core_ext/array'
[ :a, :b, :c ] >> [ 1, 2, 3 ]
#=> {:a=>1, :b=>2, :c=>3}

With it, your job can be done like this:

array = [1,2,3,4,5], [6,7,8,9,10] 
key_values = ["one","two","three","four","five"]

multi_array = array.map { |a| key_values >> a }
#=> [{"one"=>1, "two"=>2, "three"=>3, "four"=>4, "five"=>5},
     {"one"=>6, "two"=>7, "three"=>8, "four"=>9, "five"=>10}]

Upvotes: 0

Cary Swoveland
Cary Swoveland

Reputation: 110685

Now that @August has identified the problem with your code, I'd like to suggest a compact, Ruby-like way to obtain the result you want.

Code

def make_hash(array, key_values)
    array.map { |a| key_values.zip(a).to_h }
end

Example

array = [[1,2,3,4,5], [6,7,8,9,10]] 
key_values = ["one","two","three","four","five"]

make_hash(array, key_values)
  #=> [{"one"=>1, "two"=>2, "three"=>3, "four"=>4, "five"=>5},
  #    {"one"=>6, "two"=>7, "three"=>8, "four"=>9, "five"=>10}]

Explanation

The first value passed into the block by Enumerable#map is:

a = [1,2,3,4,5]

so we have

b = key_values.zip(a)
  #=> [["one", 1], ["two", 2], ["three", 3], ["four", 4], ["five", 5]]
b.to_h
  #=> {"one"=>1, "two"=>2, "three"=>3, "four"=>4, "five"=>5}

For Ruby versions < 2.0 (when Array.to_h was introduced), we'd have to write Hash(b) rather than b.to_h.

Similarly, the second value passed to the block is:

a = [6,7,8,9,10]

so

key_values.zip(a).to_h
  #=> {"one"=>6, "two"=>7, "three"=>8, "four"=>9, "five"=>10}

Upvotes: 3

August
August

Reputation: 12558

Your array has two entries of the same hash object. So if you change the hash object anywhere, it will change in both array entries. To avoid having the same exact hash object in each array entry, you can duplicate the hash before inserting, by changing multiArray << hash to multiArray << hash.dup

Upvotes: 2

Related Questions