Add a hash pluck method?

1,704 views
Skip to first unread message

Mike Campbell

unread,
Jun 25, 2015, 11:10:15 AM6/25/15
to rubyonra...@googlegroups.com
The regular AR pluck method returns the values only, for instance: Student.pluck(:firstname) => ["John", "Mike", "Simon"]

It'd be a nice feature to have a version which returns a hashes with the column names as keys: [{ firstname: "John"}, { firstname: "Mike" }, { firstname: "Simon" }]

I know this can (almost) be achieved with Student.select(:firstname).map(&:attributes) (it forces you to have an id attribute), but this involves instantiating lots of AR objects, which loses the benefit of pluck.

An example implementation would be:

def self.hash_pluck(*keys)
  pluck
(*keys).map{ |vals| Hash[keys.zip(Array(vals))] }
end

In some (probably bad) benchmarks I made comparing this to the select alternative, the select method was ~3.4x slower.

Benchmark.ips do |x|
  x
.report("hash_pluck") { Student.hash_pluck(:firstname) }
  x
.report("select") { Student.select(:firstname).map(&:attributes) }
  x
.compare!
end

Calculating -------------------------------------
             
select     6.000  i/100ms
          hash_pluck    
24.000  i/100ms
-------------------------------------------------
             
select     77.096  29.8%) i/s -    342.000
          hash_pluck    
265.316  41.8%) i/s -      1.104k

Comparison:
          hash_pluck
:      265.3 i/s
             
select:       77.1 i/s - 3.44x slower

Just thought that it might be a nice convenience method to have!

Cheers,
Mike

Ryan Bigg

unread,
Jun 25, 2015, 5:39:52 PM6/25/15
to rubyonra...@googlegroups.com
What is the benefit of this method?

You can pluck(:firstname).map { |e| { firstname: e } } if you really wanted that. Why are you plucking and wanting it to be a hash? 

 
--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-co...@googlegroups.com.
To post to this group, send email to rubyonra...@googlegroups.com.
Visit this group at http://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/d/optout.

Al Tenhundfeld

unread,
Jun 26, 2015, 2:35:30 PM6/26/15
to rubyonra...@googlegroups.com
The proposed method has more benefit when plucking multiple columns. Of course, you can still do pluck(:first, :last, :dob).map {|e| {first: e[0], last: e[1], dob: e[2]} }, but I guess that's a little cumbersome and smelly. I'm not arguing the benefit – small benefit IMO – is worth the cost of adding and maintaining for years, but I do see its convenience.

-Al

Nicholas Firth-McCoy

unread,
Jun 26, 2015, 4:52:42 PM6/26/15
to rubyonra...@googlegroups.com
I would definitely use this, given we don't currently have many good options for querying against the database and getting non-ActiveRecord objects back.

I often want a fast way to serialize stuff as JSON (ie. without instantiating AR objects and then turning them back into hashes), and doing this using `select_values` is pretty cumbersome.

For example: `render json: { tasks: current_user.tasks.hash_pluck(:id, :name, :completed) }`

That said, I'd probably favour a more general way to query the database and get back arrays/hashes over something like `hash_pluck`, which would only handle very simple cases.

Tiger Shen

unread,
Jun 28, 2015, 2:22:58 AM6/28/15
to rubyonra...@googlegroups.com
The pluck_to_hash gem (https://github.com/girishso/pluck_to_hash) encapsulates this functionality, if you're interested.
Reply all
Reply to author
Forward
0 new messages