Anthony Harvey
Posted on July 17, 2024
ActiveRecord is an important part of Ruby on Rails (the M in MVC) and is an Object Relational Mapping system (ORM) that maps the objects in your app to tables in your database. By using ActiveRecord (or any ORM) the attributes of your objects and the relationships between them can be saved and retrieved from your database without writing SQL statements. One of the helpful methods that ActiveRecord provides to help facilitate this is pluck
.
What is the pluck
method?
According to the Rails Guides:
pluck
can be used to pick the value(s) from the named column(s) in the current relation. It accepts a list of column names as an argument and returns an array of values of the specified columns with the corresponding data type.
Put another way, pluck
let's you specify one or more columns that are defined on an ActiveRecord model and returns the values in an Array.
Order.pluck(:id)
# SELECT orders.id FROM orders
=> [1, 2, 3]
Passing more than one column argument returns a nested array with values for each of the columns in each of the inner arrays. In this example the values for id
and shipped
will be in the inner arrays.
Order.pluck(:id, :status)
# SELECT orders.id, orders.status FROM orders
=> [[1, "shipped"], [2, "pending"], [3, "pending"]]
You can achieve similar results using select
and map
, but pluck
allows you to just specify the column(s) you want and get that data back.
Order.select(:id).map{ |o| o.id }
Order.select(:id).map(&:id)
Order.select(:id, :status).map{ |o| [o.id, o.status] }
Using select
and map
together like this produces the same results, but you can replace the above code with pluck
and avoid chaining multiple methods.
Plus, it's a little more concise, easier to read and usually faster.
What makes pluck
faster than select
and map
?
When using select
, it builds entire ActiveRecord objects from the database, which can be costly for large objects and for large queries if there are a lot of objects retrieved. Plus, there's the added step of mapping over the results to return the data in the shape that you want.
pluck
, on the other hand, skips creating ActiveRecord objects entirely and only returns the data from the columns that you specified.
Using pluck
to query multiple tables
You can also use pluck
to query across joined tables as well.
Order.joins(:customer).pluck("orders.id, customers.first_name")
Using pluck
on Enumerables
You can also use pluck
on Enumerables to return the values you want by specifying the key(s). You just have to require
ActiveSupport, which extends Enumerable with the pluck
method.
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
# => ["David", "Rafael", "Aaron"]
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
# => [[1, "David"], [2, "Rafael"]]
When to not use pluck
One thing to keep in mind when using pluck
is if you already have access to an Array of ActiveRecord objects that you're retrieved from the database.
Calling pluck
on that Array of ActiveRecord objects will trigger an additional query to the database. You can avoid this extra round trip to the database by using map
instead.
order_ids = []
completed_orders = Order.where(status: "completed")
completed_orders.each do |order| # .each will make a database call
order_ids << order.id
end
# will trigger an unecessary database call
completed_orders.pluck(:total)
# the completed_orders array is already loaded in memory
# and we can just map over the array
completed_orders.map(&:total)
Recent Update to pluck
A Pull Request was introduced by fatkodima and merged into Rails on April 13, 2024 to allow ActiveRecord::Base#pluck
to accept hash values!
From the PR:
# Before
Post.joins(:comments).pluck("posts.id", "comments.id", "comments.body")
# After
Post.joins(:comments).pluck(posts: [:id], comments: [:id, :body])
Resources
This article was originally published on anthonygharvey.com on July 16th, 2024.
Posted on July 17, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.