Ruby: How to collect a Hash from an Array

Updated . Posted . Visible to the public.

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

Array#to_h with a block (Ruby 2.6+)

You can call an array with a block that is called with each element. The block must return a [key, value] tuple.

This is useful if both the hash key and value can be derived from each array element:

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

Array#to_h on an array of key/value tuples (Ruby 2.1+)

Converts an Array of [key, value] tuples into a Hash.

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

Enumerable#index_by (any Rails version)

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

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">]
}

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
Last edit
Henning Koch
Attachments
Keywords
list, map
License
Source code in this card is licensed under the MIT License.
Posted by Henning Koch to makandra dev (2010-09-14 11:31)