Ruby hashes are awesome! They allow us to efficiently access data by a key. A common task in data processing is to build hashes using Enumerable#each_with_object so we can quickly access related data during processing steps
In this example, we need to process a large amount of photo data by invoking process_photo, which needs a photo from our data and an instance from our Photographer model
-- CODE language-ruby line-numbers --# Bad, we will perform photo_data.count database requests
photo_data.each do |photo|
process_photo(photo, Photographer.find_by(key: photo.key))
end
-- CODE language-ruby line-numbers --# Better, we perform a single database request
all_keys = photo_data.map(&:key).uniq
photographers_by_key = Photographer
.where(key: all_keys)
.each_with_index({}) do |photographer, accumulator|
accumulator[photographer.key] = photographer
end
photo_data.each do |photo|
process_photo(photo, photographers_by_key[photo.key])
end
Thanks to a recent addition to rubocop-rails, I learned that we can use Rails’ ActiveSupport method Enumerable#index_by to make this manipulation even easier
-- CODE language-ruby line-numbers --# Even Better, Enumerable#index_by simplifies the construction of our hash
all_keys = photo_data.map(&:key).uniq
photographers_by_key = Photographer.where(key: all_keys).index_by(&:key)
photo_data.each do |photo|
process_photo(photo, photographers_by_key[photo.key])
end
TLDR: use Enumerable#index_by over Enumerable#each_with_object
-- CODE language-ruby line-numbers --# Good
photographers_by_key = Photographer
.where(key: all_keys)
.each_with_index({}) do |photographer, accumulator|
accumulator[photographer.key] = photographer
end
-- CODE language-ruby line-numbers --# Better
photographers_by_key = Photographer.where(key: all_keys).index_by(&:key)
Are you ready to build something brilliant? We're ready to help.