开发者

Rails - given an array of Users - how to get a output of just emails?

开发者 https://www.devze.com 2023-02-14 17:43 出处:网络
I have the following: @users = User.all User has several fields including email. What I would l开发者_开发问答ike to be able to do is get a list of all the @users emails.

I have the following:

@users = User.all

User has several fields including email.

What I would l开发者_开发问答ike to be able to do is get a list of all the @users emails.

I tried:

@users.email.all but that errors w undefined

Ideas? Thanks


(by popular demand, posting as a real answer)

What I don't like about fl00r's solution is that it instantiates a new User object per record in the DB; which just doesn't scale. It's great for a table with just 10 emails in it, but once you start getting into the thousands you're going to run into problems, mostly with the memory consumption of Ruby.

One can get around this little problem by using connection.select_values on a model, and a little bit of ARel goodness:

User.connection.select_values(User.select("email").to_sql)

This will give you the straight strings of the email addresses from the database. No faffing about with user objects and will scale better than a straight User.select("email") query, but I wouldn't say it's the "best scale". There's probably better ways to do this that I am not aware of yet.

The point is: a String object will use way less memory than a User object and so you can have more of them. It's also a quicker query and doesn't go the long way about it (running the query, then mapping the values). Oh, and map would also take longer too.

If you're using Rails 2.3...

Then you'll have to construct the SQL manually, I'm sorry to say.

User.connection.select_values("SELECT email FROM users")

Just provides another example of the helpers that Rails 3 provides.


I still find the connection.select_values to be a valid way to go about this, but I recently found a default AR method that's built into Rails that will do this for you: pluck.

In your example, all that you would need to do is run:

User.pluck(:email)

The select_values approach can be faster on extremely large datasets, but that's because it doesn't typecast the returned values. E.g., boolean values will be returned how they are stored in the database (as 1's and 0's) and not as true | false.

The pluck method works with ARel, so you can daisy chain things:

User.order('created_at desc').limit(5).pluck(:email)


User.select(:email).map(&:email)


Just use:

User.select("email")


While I visit SO frequently, I only registered today. Unfortunately that means that I don't have enough of a reputation to leave comments on other people's answers.

Piggybacking on Ryan's answer above, you can extend ActiveRecord::Base to create a method that will allow you to use this throughout your code in a cleaner way.

Create a file in config/initializers (e.g., config/initializers/active_record.rb):

class ActiveRecord::Base
  def self.selected_to_array
    connection.select_values(self.scoped)
  end
end

You can then chain this method at the end of your ARel declarations:

User.select('email').selected_to_array
User.select('email').where('id > ?', 5).limit(4).selected_to_array


Use this to get an array of all the e-mails:

@users.collect { |user| user.email }
# => ["test@example.com", "test2@example.com", ...]

Or a shorthand version:

@users.collect(&:email)


You should avoid using User.all.map(&:email) as it will create a lot of ActiveRecord objects which consume large amounts of memory, a good chunk of which will not be collected by Ruby's garbage collector. It's also CPU intensive.

If you simply want to collect only a few attributes from your database without sacrificing performance, high memory usage and cpu cycles, consider using Valium.

https://github.com/ernie/valium

Here's an example for getting all the emails from all the users in your database.

User.all[:email]

Or only for users that subscribed or whatever.

User.where(:subscribed => true)[:email].each do |email|
  puts "Do something with #{email}"
end

Using User.all.map(&:email) is considered bad practice for the reasons mentioned above.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号