Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert array-of-hashes to a hash-of-hashes, indexed by an attribute of the hashes

I've got an array of hashes representing objects as a response to an API call. I need to pull data from some of the hashes, and one particular key serves as an id for the hash object. I would like to convert the array into a hash with the keys as the ids, and the values as the original hash with that id.

Here's what I'm talking about:

api_response = [
  { :id => 1, :foo => 'bar' },
  { :id => 2, :foo => 'another bar' },
  # ..
]

ideal_response = {
  1 => { :id => 1, :foo => 'bar' },
  2 => { :id => 2, :foo => 'another bar' },
  # ..
}

There are two ways I could think of doing this.

  1. Map the data to the ideal_response (below)
  2. Use api_response.find { |x| x[:id] == i } for each record I need to access.
  3. A method I'm unaware of, possibly involving a way of using map to build a hash, natively.

My method of mapping:

keys = data.map { |x| x[:id] }
mapped = Hash[*keys.zip(data).flatten]

I can't help but feel like there is a more performant, tidier way of doing this. Option 2 is very performant when there are a very minimal number of records that need to be accessed. Mapping excels here, but it starts to break down when there are a lot of records in the response. Thankfully, I don't expect there to be more than 50-100 records, so mapping is sufficient.

Is there a smarter, tidier, or more performant way of doing this in Ruby?

like image 375
coreyward Avatar asked Jan 20 '11 23:01

coreyward


People also ask

How do you turn an array into a hash in Ruby?

How do you turn an array into a hash in Ruby? The to_h method is defined in the array class. It works to convert an array to a hash in the form of key-value pairs. The method converts each nested array into key-value pairs.

Can an array be a hash key?

All lists, mutable or not, hash by value. And you can use e.g. mutable ArrayList as hash keys.

What is the difference between hashes and arrays?

In arrays, you have to loop over all items before you find what you are looking for while in a hash table you go directly to the location of the item. Inserting an item is also faster in Hash tables since you just hash the key and insert it.

What is hash array?

An array of hashes in the Perl language stores data that you would like to access in a sequential manner. Each of the array indices has key/value pairs for hashes. The general syntax for arrays that consist of hashes is as shown below: @Arrays = (


1 Answers

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
like image 108
tokland Avatar answered Oct 25 '22 14:10

tokland