Read more

How to collect a Hash from an Array

Henning Koch
September 14, 2010Software engineer at makandra GmbH

There are many different methods that allow mapping an Array to a Hash in Ruby.

Enumerable#index_by (any Rails)

users = User.all
users_by_id = users.index_by(&:id)
{
  1 => #<User id: 1, name: "Alice">,
  2 => #<User id: 2, name: "Bob"> 
}
Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

In case of a duplicate, the last entry wins.

Enumerable#group_by (Ruby 1.8.7+)

Use group_by Show archive.org snapshot when duplicates are possible.
Hash values are always an Array of elements per group.

users = User.all
users_by_name = users.group_by(&:name)
{
  "Alice" => [#<User id: 1, name: "Alice">],
  "Bob" => [#<User id: 2, name: "Bob">]
}

Array#to_h (Ruby 2.1+)

Converts an Array of tuples into a Hash.
This is especially useful if the array's elements need to be transformed first.

users = User.all
user_names_by_id = users.map { |user| [user.id, user.name] }.to_h
{
  1 => "Alice",
  2 => "Bob"
}

Enumerable#index_with (Rails 6+)

To generate a hash where array elements become hash keys, and values are calculated from them, use index_with.

users = User.all
user_ids_by_user = users.index_with(&:name)
{
  #<User id: 1, name: "Alice"> => "Alice",
  #<User id: 2, name: "Bob"> => "Bob"
}

Legacy solution: collect_hash monkey patch

The attached initializer will let you create a hash from an array in a single method call.

  • Copy the attached initializer to config/initializers
  • All enumerables (arrays, sets, scopes, etc.) now have an additional method collect_hash
  • The method takes a block. The block is called for each element in the array and should return a 2-tuple where the first element is the key and the second element is the value.
  • A key/value entry is skipped when the block returns nil.

Example

The following will turn a list of User records into a hash where the keys are the users' ids and the values are the users' attribute hashes:

users = User.all
user_plans = users.collect_hash do |u|
  [u.id, u.attributes]
end
Henning Koch
September 14, 2010Software engineer at makandra GmbH
Posted by Henning Koch to makandra dev (2010-09-14 13:31)