In Ruby, How to Make a Hash from an Array

In Ruby, how do I make a hash from an array?

Say you have a function with a funtastic name: "f"

def f(fruit)
fruit + "!"
end

arr = ["apples", "bananas", "coconuts", "watermelons"]
h = Hash[ *arr.collect { |v| [ v, f(v) ] }.flatten ]

will give you:

{"watermelons"=>"watermelons!", "bananas"=>"bananas!", "apples"=>"apples!", "coconuts"=>"coconuts!"}

Updated:

As mentioned in the comments, Ruby 1.8.7 introduces a nicer syntax for this:

h = Hash[arr.collect { |v| [v, f(v)] }]

Create a hash from an array of keys

My solution, one among the others :-)

a = ["a", "b", "c", "d"]
h = Hash[a.map {|x| [x, 1]}]

Hash of Arrays in Ruby

This should show you what is happening (object_id is your friend). (I've inserted an underscore in the Object_id to make it easier to see differences.)

hash1 = {}            # => {} 
arr = ["v1", "v2"] # => ["v1", "v2"]
arr.object_id # => 7016637_4343580
hash1["k1"] = arr # => ["v1", "v2"]
hash1 # => {"k1"=>["v1", "v2"]}
hash1["k1"].object_id # => 7016637_4343580
arr.clear # => []
arr.object_id # => 7016637_4343580
arr << "v3" << "v4" # => ["v3", "v4"]
arr.object_id # => 7016637_4343580
hash1["k2"] = arr # => ["v3", "v4"]
hash1 # => {"k1"=>["v3", "v4"], "k2"=>["v3", "v4"]}
hash1["k1"].object_id # => 7016637_4343580
hash1["k2"].object_id # => 7016637_4166580

arr = [] # => []
arr.object_id # => 7016637_4036500
arr = ["v5", "v6"] # => ["v5", "v6"]
arr.object_id # => 7016637_3989880
hash1 # => {"k1"=>["v3", "v4"], "k2"=>["v3", "v4"]}
hash1["k1"].object_id # => 7016637_4343580
hash1["k2"] = arr # => ["v5", "v6"]
hash1 # => {"k1"=>["v3", "v4"], "k2"=>["v5", "v6"]}
hash1["k1"].object_id # => 7016637_4343580
hash1["k2"].object_id # => 7016637_3989880

Creating array of hashes in ruby

Assuming what you mean by "rowofrows" is an array of arrays, heres a solution to what I think you're trying to accomplish:

array_of_arrays = [["abc",9898989898,"abc@xyz.com"], ["def",9898989898,"def@xyz.com"]]

array_of_hashes = []
array_of_arrays.each { |record| array_of_hashes << {'name' => record[0], 'number' => record[1].to_i, 'email' => record[2]} }

p array_of_hashes

Will output your array of hashes:

[{"name"=>"abc", "number"=>9898989898, "email"=>"abc@xyz.com"}, {"name"=>"def", "number"=>9898989898, "email"=>"def@xyz.com"}]

Dynamically create hash from array of arrays

The code you've provided can be modified to merge hashes on conflict instead of overwriting:

values.each do |row|
Hash[*row].each do |key, value|
keys = key.split(':')

if !data.dig(*keys)
hh = keys.reverse.inject(value) { |a, n| { n => a } }
data.merge!(hh) { |_, old, new| old.merge(new) }
end
end
end

But this code only works for the two levels of nesting.

By the way, I noted ruby-on-rails tag on the question. There's deep_merge method that can fix the problem:

values.each do |row|
Hash[*row].each do |key, value|
keys = key.split(':')

if !data.dig(*keys)
hh = keys.reverse.inject(value) { |a, n| { n => a } }
data.deep_merge!(hh)
end
end
end

Convert Array to Hash while preserving Array index values in Ruby

Using Enumerable#each_with_index:

Hash[array.each_with_index.map { |value, index| [index, value] }]
# => {0=>"Adult", 1=>"Family", 2=>"Single", 3=>"Child"}

As @hirolau commented, each_with_index.map can also be written as map.with_index.

Hash[array.map.with_index { |value, index| [index, value] }]
# => {0=>"Adult", 1=>"Family", 2=>"Single", 3=>"Child"}

UPDATE

Alterantive that use Hash#invert:

Hash[array.map.with_index{|*x|x}].invert
# => {0=>"Adult", 1=>"Family", 2=>"Single", 3=>"Child"}
Hash[[*array.map.with_index]].invert
# => {0=>"Adult", 1=>"Family", 2=>"Single", 3=>"Child"}

Creating a Hash with values as arrays and default value as empty array

Lakshmi is right. When you created the Hash using Hash.new([]), you created one array object.

Hence, the same array is returned for every missing key in the Hash.

That is why, if the shared array is edited, the change is reflected across all the missing keys.

Using:

Hash.new { |h, k| h[k] = [] }

Creates and assigns a new array for each missing key in the Hash, so that it is a unique object.

Create a hash out of an array where the values are the indices of the elements

I'm adding my two cents:

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

hash = {}
array.map.with_index {|val, idx| [val, idx]}.group_by(&:first).map do |k, v|
hash[k] = v[0][1] if v.size == 1
hash[k] = v.map(&:last) if v.size > 1
end

p hash #=> {1=>0, 3=>1, 4=>2, 5=>3, 6=>[4, 5, 6], 8=>[7, 8, 9], 9=>10, 7=>[11, 12, 13]}

It fails with duplicated element not adjacent, of course.

This is the expanded version, step by step, to show how it works.

The basic idea is to build a temporary array with pairs of value and index, then work on it.

array = [1,3,4,5,6,6,6]

tmp_array = []
array.each_with_index do |val, idx|
tmp_array << [val, idx]
end
p tmp_array #=> [[1, 0], [3, 1], [4, 2], [5, 3], [6, 4], [6, 5], [6, 6]]

tmp_hash = tmp_array.group_by { |e| e[0] }
p tmp_hash #=> {1=>[[1, 0]], 3=>[[3, 1]], 4=>[[4, 2]], 5=>[[5, 3]], 6=>[[6, 4], [6, 5], [6, 6]]}

hash = {}
tmp_hash.map do |k, v|
hash[k] = v[0][0] if v.size == 1
hash[k] = v.map {|e| e[1]} if v.size > 1
end

p hash #=> {1=>1, 3=>3, 4=>4, 5=>5, 6=>[4, 5, 6]}

It can be written as one line as:

hash = {}
array.map.with_index.group_by(&:first).map { |k, v| v.size == 1 ? hash[k] = v[0][1] : hash[k] = v.map(&:last) }
p hash


Related Topics



Leave a reply



Submit