84

Right now I'm doing something like this to select a single column of data:

points = Post.find_by_sql("select point from posts")

Then passing them to a method, I'd like my method to remain agnostic, and now have to call hash.point from within my method. How can I quickly convert this into an array and pass the data set to my method, or is there a better way?

franklin stine
  • 1,100
  • 1
  • 8
  • 11

4 Answers4

198

In Rails 3.2 there is a pluck method for this

Just like this:

Person.pluck(:id) # SELECT people.id FROM people
Person.pluck(:role).uniq # unique roles from array of people
Person.distinct.pluck(:role) # SELECT DISTINCT role FROM people SQL
Person.where(:confirmed => true).limit(5).pluck(:id)

Difference between uniq and distinct

BDC
  • 63
  • 1
  • 9
alony
  • 10,725
  • 3
  • 39
  • 46
17

You should use the pluck method as @alony suggested. If you are stuck before Rails 3.2 you can use the ActiveRecord select method together with Array#map:

Post.select(:point).map(&:point)
#=> ["foo", "bar", "baz"] 

before Ruby 1.9 you'd have to do .map{|x| x.title} though, because Symbol#to_proc (aliased by the unary & operator) is not defined in earlier versions of Ruby.

Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168
5

If you see the definition of select_values , then it using 'map(&:field_name)'

  def select_values(arel, name = nil)
    result = select_rows(to_sql(arel), name)
    result.map { |v| v[0] }
  end

The common and general Rails way to collect all the fields values in array is like :

points = Post.all(:select => 'point').map(&:point)
Vik
  • 5,931
  • 3
  • 31
  • 38
  • Good point. Thank you. This works great, and lets me still apply order and other things to the query, unlike the Post.select example. – franklin stine Mar 26 '12 at 10:47
  • I dont agree with you, @franklinstine: `Post.select(:point).limit(1)` performs `SELECT point FROM "posts" LIMIT 1` whereas `Post.all(:select => :point).limit(1)` raises a `NoMethodError`. Please correct me if i'm wrong. – Patrick Oscity Mar 26 '12 at 12:53
  • I agree with your comments. But I am not getting where mention in the post to use : Post.all(:select => :point).limit(1) Ofcourse it will give error "undefined method `limit'" – Vik Mar 26 '12 at 13:15
  • @franklinstine said he prefers your method because he could chain other statements after it, i just wanted to make clear that this is not true – Patrick Oscity Mar 26 '12 at 13:57
  • Yes @padde you mis-inferred. I was saying you can still apply order and other query statements such as: Post.all(:select => 'score', :order => 'score ASC').map(&:score). – franklin stine Mar 26 '12 at 14:13
  • Ok, now i see. I have to admit though, that using the `pluck` method as @alony suggested is the most elegant way to do this. – Patrick Oscity Mar 26 '12 at 14:19
3
points = Post.all.collect {|p| p.point}
rajibchowdhury
  • 5,264
  • 2
  • 14
  • 7
  • 2
    This works, but you select all fields from the database and then filter the result in Ruby whereas the `select` statement only fetches the specified columns. – Patrick Oscity Mar 26 '12 at 10:03