Reputation: 1364
How can I turn:
Person.all.pluck(:id, :name)
to
[{id: 1, name: 'joe'}, {id: 2, name: 'martin'}]
without having to .map every value (since when I add or remove from the .pluck I have to do he same with the .map)
Upvotes: 47
Views: 35824
Reputation: 106952
I see three options:
1) pluck
plus map
:
Person.pluck(:id, :name).map { |p| { id: p[0], name: p[1] } }
2) pluck
plus map
with the latest Ruby syntax:
Person.pluck(:id, :name).map { |id, name| { id:, name: } }
3) pluck
plus map
plus zip
and a variable to make it DRY-er:
attrs = %w(id name)
Person.pluck(*attrs).map { |p| attrs.zip(p).to_h }
4) or you might not use pluck
at all, although this is much less performant:
Person.all.map { |p| p.slice(:id, :name) }
Upvotes: 24
Reputation: 160
I know it's an old thread but in case someone is looking for simpler version of this
Hash[Person.all(:id, :name)]
Tested in Rails 5.
Upvotes: 1
Reputation: 4970
Here is a method that has worked well for me:
def pluck_to_hash(enumerable, *field_names)
enumerable.pluck(*field_names).map do |field_values|
field_names.zip(field_values).each_with_object({}) do |(key, value), result_hash|
result_hash[key] = value
end
end
end
Upvotes: 1
Reputation: 355
The easiest way is to use the pluck
method combined with the zip
method.
attrs_array = %w(id name)
Person.all.pluck(attrs_array).map { |ele| attrs_array.zip(ele).to_h }
You can also create a helper method if you are using this method through out your application.
def pluck_to_hash(object, *attrs)
object.pluck(*attrs).map { |ele| attrs.zip(ele).to_h }
end
Consider modifying by declaring self
as the default receiver rather than passing Person.all
as the object
variable.
Read more about zip.
Upvotes: 1
Reputation: 119
If you have multiple attributes, you may do this for cleanliness:
Item.pluck(:id, :name, :description, :cost, :images).map do |item|
{
id: item[0],
name: item[1],
description: item[2],
cost: item[3],
images: item[4]
}
end
Upvotes: 1
Reputation: 2241
If you use postgresql, you can use json_build_object
function in pluck method:
https://www.postgresql.org/docs/9.5/functions-json.html
That way, you can let db create hashes.
Person.pluck("json_build_object('id', id, 'name', name)")
#=> [{id: 1, name: 'joe'}, {id: 2, name: 'martin'}]
Upvotes: 11
Reputation: 1403
There is pluck_all gem that do almost the same thing as pluck_to_hash do. And it claims that it's 30% faster. (see the benchmark here).
Usage:
Person.pluck_all(:id, :name)
Upvotes: 3
Reputation: 8646
You can map
the result:
Person.all.pluck(:id, :name).map { |id, name| {id: id, name: name}}
As mentioned by @alebian: This is more efficient than
Person.all.as_json(only: [:id, :name])
Reasons:
pluck
only returns the used columns (:id, :name) whereas the other solution returns all columns. Depending on the width of the table (number of columns) this makes quite a differencePerson
objects, does not need to assign attributes to the models and so on. Instead it just returns an array with one integer and one string.as_json
again has more overhead than the simple map
as it is a generic implementation to convert a model to a hashUpvotes: 56
Reputation: 352
You can use the aptly-named pluck_to_hash gem for this: https://github.com/girishso/pluck_to_hash
It will extend AR with pluck_to_hash method that works like this:
Post.limit(2).pluck_to_hash(:id, :title)
#
# [{:id=>213, :title=>"foo"}, {:id=>214, :title=>"bar"}]
#
Post.limit(2).pluck_to_hash(:id)
#
# [{:id=>213}, {:id=>214}]
It claims to be several times faster than using AR select
and as_json
Upvotes: 3
Reputation: 999
Could go for a hash after the pluck with the ID being the key and the Name being the value:
Person.all.pluck(:id, :name).to_h
{ 1 => 'joe', 2 => 'martin' }
Not sure if this fits your needs, but presenting as an option.
Upvotes: 7
Reputation: 1115
You could simply do this
Person.select(:id,:name).as_json
You could try this as well
Person.all.as_json(only: [:id, :name])
Upvotes: 44