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
Testing If a Hash Has Any of a Number of Keys
Ruby: Creating a Sandboxed Eval
How to Create Wizard Forms in Ruby on Rails
Linkedin API for Company Directory
Get All Instance Variables Declared in Class
Is There a Way in Ruby 1.9 to Remove Invalid Byte Sequences from Strings
How to Skip Has_Secure_Password Validations
Sinatra Configuring Environments on the Fly
Reading and Updating Yaml File by Ruby Code
Is Ruby Really an Interpreted Language If All of Its Implementations Are Compiled into Bytecode
Rvm Does Not Install Ruby 1.9.2 on Snow Leopard: 'Error Running 'Make '
Getting Uninitialized Constant Error When Trying to Run Tests
How Does Require Rubygems Help Find Rubygem Files
Extract All Email Addresses from Some .Txt Documents Using Ruby