Search code examples
ruby-on-railsjoinvirtual-attribute

How can I get a unique :group of a virtual attribute in rails?


I have several similar models ContactEmail, ContactLetter, etcetera.

Each one belongs_to a Contact

Each contact belongs_to a Company

So, what I did was create a virtual attribute for ContactEmail:

  def company_name
    contact = Contact.find_by_id(self.contact_id)

    return contact.company_name

  end

Question: How can I get an easy list of all company_name (without duplicates) if I have a set of ContactEmails objects (from a find(:all) method, for example)?

When I try to do a search on ContactEmail.company_name using the statistics gem, for example, I get an error saying that company_name is not a column for ContactEmail.


Solution

  • Assuming your ContactEmail set is in @contact_emails (untested):

    @contact_emails.collect { |contact_email| contact_email.company_name }.uniq

    You don't need the virtual attribute for this purpose though. ActiveRecord sets up the relationship automatically based on the foreign key, so you could take the company_name method out of the ContactEmail model and do:

    @contact_emails.collect { |contact_email| contact_email.contact.company_name }.uniq

    Performance could be a consideration for large sets, so you might need to use a more sophisticated SQL query if that's an issue.

    EDIT to answer your 2nd question

    If company_name is a column, you can do:

    ContactEmail.count(:all, :joins => :contact, :group => 'contact.company_name')

    On a virtual attribute I think you'd have to retrieve the whole set and use Ruby (untested):

    ContactEmail.find(:all, :joins => :contact, :select => 'contacts.company_name').group_by(&:company_name).inject({}) {|hash,result_set| hash.merge(result_set.first=>result_set.last.count)}

    but that's not very kind to the next person assigned to maintain your system -- so you're better off working out the query syntax for the .count version and referring to the column itself.