Convert array-of-hashes to a hash-of-hashes, indexed by an attribute of the hashes
Ruby <= 2.0
> Hash[api_response.map { |r| [r[:id], r] }]
#=> {1=>{:id=>1, :foo=>"bar"}, 2=>{:id=>2, :foo=>"another bar"}}
However, Hash::[] is pretty ugly and breaks the usual left-to-right OOP flow. That's why Facets proposed Enumerable#mash:
> require 'facets'
> api_response.mash { |r| [r[:id], r] }
#=> {1=>{:id=>1, :foo=>"bar"}, 2=>{:id=>2, :foo=>"another bar"}}
This basic abstraction (convert enumerables to hashes) was asked to be included in Ruby long ago, alas, without luck.
Note that your use case is covered by Active Support: Enumerable#index_by
Ruby >= 2.1
[UPDATE] Still no love for Enumerable#mash
, but now we have Array#to_h. It creates an intermediate array, but it's better than nothing:
> object = api_response.map { |r| [r[:id], r] }.to_h
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"}
Ruby -- convert array to hash with specific index
Try the below :
a = [
{
'date' => '2013-09-01',
'impressions' => 50,
'clicks' => 20,
},
{
'date' => '2013-09-02',
'impressions' => 51,
'clicks' => 22,
},
]
Hash[a.map{|h| [h['date'],Hash[h.to_a[1..-1]]] }]
or
Hash[a.map{|h| [h['date'],h.select{|k,_| k != 'date'}] }]
# => {"2013-09-01"=>{"impressions"=>50, "clicks"=>20},
#"2013-09-02"=>{"impressions"=>51, "clicks"=>22}}
What is the best way to convert an array to a hash in Ruby
NOTE: For a concise and efficient solution, please see Marc-André Lafortune's answer below.
This answer was originally offered as an alternative to approaches using flatten, which were the most highly upvoted at the time of writing. I should have clarified that I didn't intend to present this example as a best practice or an efficient approach. Original answer follows.
Warning! Solutions using flatten will not preserve Array keys or values!
Building on @John Topley's popular answer, let's try:
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
This throws an error:
ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10
The constructor was expecting an Array of even length (e.g. ['k1','v1,'k2','v2']). What's worse is that a different Array which flattened to an even length would just silently give us a Hash with incorrect values.
If you want to use Array keys or values, you can use map:
h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
This preserves the Array key:
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
Convert array of 2-element arrays into a hash, where duplicate keys append additional values
Using functional baby steps:
irb:01.0> array = [[:a,:b],[:a,:c],[:c,:b]]
#=> [[:a, :b], [:a, :c], [:c, :b]]
irb:02.0> array.group_by(&:first)
#=> {:a=>[[:a, :b], [:a, :c]], :c=>[[:c, :b]]}
irb:03.0> array.group_by(&:first).map{ |k,a| [k,a.map(&:last)] }
#=> [[:a, [:b, :c]], [:c, [:b]]]
irb:04.0> Hash[ array.group_by(&:first).map{ |k,a| [k,a.map(&:last)] } ]
#=> {:a=>[:b, :c], :c=>[:b]}
Using imperative style programming:
irb:10.0> h = Hash.new{ |h,k| h[k]=[] }
#=> {}
irb:11.0> array.each{ |k,v| h[k] << v }
#=> [[:a, :b], [:a, :c], [:c, :b]]
irb:12.0> h
#=> {:a=>[:b, :c], :c=>[:b]}
As an imperative one-liner:
irb:13.0> h = Hash.new{ |h,k| h[k]=[] }.tap{ |h| array.each{ |k,v| h[k] << v } }
#=> {:a=>[:b, :c], :c=>[:b]}
Or using everyone's favorite inject
:
irb:14.0> array.inject(Hash.new{ |h,k| h[k]=[] }){ |h,(k,v)| h[k] << v; h }
#=> {:a=>[:b, :c], :c=>[:b]}
If you really want to have single values not collided as an array, you can either un-array them as a post-processing step, or use a different hash accumulation strategy that only creates an array upon collision. Alternatively, wrap your head around this:
irb:17.0> hashes = array.map{ |pair| Hash[*pair] } # merge many mini hashes
#=> [{:a=>:b}, {:a=>:c}, {:c=>:b}]
irb:18.0> hashes.inject{ |h1,h2| h1.merge(h2){ |*a| a[1,2] } }
#=> {:a=>[:b, :c], :c=>:b}
Using #select on either a hash or an array of hashes
You could do
[my_hash[:test]].flatten.select { |s| s[:another_test] == 123 }
How to convert an array to a hash using Mongoid Pipeline?
Use $map to map your array from ['v1', 'v2', 'v3']
to [['v1', true], ['v2', true], ['v3', true]]
, then use https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/ to convert that to a hash.
Rails convert computed array of hashes into a nested array
It's actually simple:
records.map { |h| [h['created_at'], h['delta']] }
=> [["2017-02-26 18:50:20.654996", nil],
["2017-02-27 18:50:20.654996", 2000],
["2017-02-28 18:50:20.654996", 3000]]
In Ruby, you can access hash value by passing key name in brackets, like in this instance.
Or you can use Hash#values_at
:
records.map { |h| h.values_at('created_at', 'delta') }
and get the same result.
Related Topics
How to 'Unload' ('Un-Require') a Ruby Library
In Ruby or Rails, Why Is "Include" Sometimes Inside the Class and Sometimes Outside the Class
Is Getting Converted as "\U0092" by Nokogiri in Ruby on Rails
Ruby Ssl with Twitter Failed on Cert Openssl Issue on Windows 7
Rails: How to Print a Decimal as a Percent
How to Get the Destination Url of a Shortened Url Using Ruby
How to Require File from 'Gem' Which Are Not Under 'Lib' Directory
Rails 4 Activerecord Typeerror Nil Is Not a Symbol
What's a Rails Plugin, or Ruby Gem, to Automatically Fix English Grammar
Rails Initializes Extremely Slow on Ruby 1.9.1
Get Response Headers from Curb
Catching Timeout Errors with Ruby Mechanize
Ruby: Is Variable Is Object in Ruby
How to Run All Ruby Scripts with Warnings
Rails 4: How to Upload Files with Ajax