Lucas Steffen
Lucas Steffen

Reputation: 1364

Ruby on Rails 4: Pluck results to hash

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

Answers (11)

spickermann
spickermann

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

Marelons
Marelons

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

Keith Bennett
Keith Bennett

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

Abeid Ahmed
Abeid Ahmed

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

muneebahmad
muneebahmad

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

mnishiguchi
mnishiguchi

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

khiav reoy
khiav reoy

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

Pascal
Pascal

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 difference
  • The pluck solution does not instantiate Person 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 hash

Upvotes: 56

Taufiq Muhammadi
Taufiq Muhammadi

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

MTarantini
MTarantini

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

Rohit Jangid
Rohit Jangid

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

Related Questions