Differencebetween Pluck and Collect in Rails

What is the difference between pluck and collect in Rails?

pluck is on the db level. It will only query the particular field. See this.

When you do:

 User.first.gifts.collect(&:id)

You have objects with all fields loaded and you simply get the id thanks to the method based on Enumerable.

So:

  • if you only need the id with Rails 4, use ids: User.first.gifts.ids

  • if you only need some fields with Rails 4, use pluck: User.first.gifts.pluck(:id, :name, ...)

  • if you only need one field with Rails 3, use pluck: User.first.gifts.pluck(:id)

  • if you need all fields, use collect

  • if you need some fields with Rails 4, still use pluck

  • if you need some fields with Rails 3, use selectand collect

Which one is faster between map, collect, select and pluck?

Usage of any of these methods requires different use cases:

Both select and pluck make SQL's SELECT of specified columns (SELECT "users"."name" FROM "users"). Hence, if you don't have users already fetched and not going to, these methods will be more performant than map/collect.

The difference between select and pluck:

  • Performance: negligible when using on a reasonable number of records
  • Usage: select returns the list of models with the column specified, pluck returns the list of values of the column specified. Thus, again, the choice depends on the use case.

collect/map methods are actually aliases, so there's no difference between them. But to iterate over models they fetch the whole model (not the specific column), they make SELECT "users".* FROM "users" request, convert the relation to an array and map over it.

This might be useful, when the relation has already been fetched. If so, it won't make additional requests, what may end up more performant than using pluck or select. But, again, must be measured for a specific use case.

ActiveRecords select(:id).collect vs. pluck(:id) methods: Why is pure AR pluck slower?

Your benchmark is inaccurate. First of all, as you can see, both executions on the database side triggers the same query

SELECT "articles"."id" FROM "articles"

Therefore, the database time should be considered irrelevant. Clearly the two queries had different execution time as shown by the console, but this is normal as if you run the same query 100 times the execution time can be different each time as it depends by a variety of variables such as the machine load, the database state, etc.

Since the database execution time can be considered equivalent, it's irrelevant for the benchmark.

Therefore, what you need to compare is the Ruby execution time and allocation. Pluck is supposed to be faster and more lightweight as compared to collect it doesn't allocate ActiveRecord objects, rather it returns only the selected values.

If you really want to benchmark the methods, you should mock the database time (which is clearly variable but irrelevant for this benchmark) and only benchmark allocation and the two different Ruby methods.

Long story short, pluck is generally more efficient.

Pluck in a selected object

Pluck will not work in that case. So instead of using pluck:

User.find([1,2,3]).pluck(:name)

Try using map:

User.find([1,2,3]).map(&:name)

What is simplest way to convert :pluck or :collect results to array of strings in Rails?

Product.first.records.pluck(:price).map(&:to_s)

`Pluck` but for a single retrieved record?

Seems like there are a decent number of options but generally speaking what you are talking about is a presenter pattern.

If you wanted a Hash from your model that would be simple:

def details
attributes.slice('id', 'oauth_id', 'oauth_expires_at',
'sync_emails', 'last_email_sync', 'manual_query')
end

If you wanted a more formal object then this is where the presenter comes in (there are many libraries for this but we will just write our own for the sake of this question)

class OauthUserPresenter
WHITELIST = %w(id oauth_id oauth_expires_at sync_emails last_email_sync manual_query)
WHITELIST.each {|attr| define_method(attr) {@_exposed_attributes[attr]}}

def initialize(user)
@_exposed_attributes = user.attributes.slice(WHITELIST)
end

def to_hash #this will allow for #as_json and #to_json
@_exposed_attributes
end

end

Then you can call as

o = OauthUserPresenter.new(OauthUser.first)

now o is technically a read only presenter object. None of the other attributes will be exposed and you can still access the methods as id, oauth_id, etc.

Presenters also allow you to add other methods without cluttering the model e.g.

class OauthUserPresenter
def expired?
begin
Time.parse(oauth_expires_at) <= Time.now
rescue TypeError, ArgumentError #handle nil, empty string, and invalid times
true
end
end
end

Rails Query group and pluck to Return Array of IDs grouped by another attribute?

At least in newer versions of Rails and PostgreSQL this is possible:

pairs = Model.select(:account_id, 'array_agg(id)')
.group(:account_id)
.pluck(:account_id, 'array_agg(id)')

This returns:

[
[1, [535, 536]],
[2, [542, 567, 588]],
]

which can be converted into a hash:

Hash[*pairs.flatten(1)]
{
1 => [535, 536],
2 => [542, 567, 588],
}

array_agg is a PostgreSQL function, but I assume MySQL's GROUP_CONCAT would work, too.

Rails .pluck on joined tables with columns of the same name returns one value and then nil

This is a bit tricky. Since there's an INNER JOIN the query produces only one id in this instance. You can actually form query the other way around:

Feature.joins(:experiment)
.where(features: { experiment_id: 1 })
.pluck(:id, :experiment_id)

Or:

Feature.joins(:experiment)
.where(experiments: { id: 1 })
.pluck(:id, :experiment_id)


Related Topics



Leave a reply



Submit