DEV Community

Anthony Harvey
Anthony Harvey

Posted on • Originally published at anthonygharvey.com

Ruby on Rails pluck method

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]
Enter fullscreen mode Exit fullscreen mode

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"]]
Enter fullscreen mode Exit fullscreen mode

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] }
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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"]]
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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])
Enter fullscreen mode Exit fullscreen mode

Resources

  1. RailsGuides: Finding by SQL - pluck

  2. Rails API Docs - Active Record Calculations - pluck

This article was originally published on anthonygharvey.com on July 16th, 2024.

Top comments (0)