How to get the children of a *collection* of parent objects?

I frequently need to do something like the following (pseudo-code):

Company.find(:conditions => ["city == 'Sandusky']).employees

If i understood it well, you have two tables, Companies and employees, and you want to get all the employees from several companies given some conditions.

ActiveRecord makes it pretty-straight forward to get this.

Employee.find(:all, :include=>:company, :conditions=>"companies.city='Sandusky'")

If you want to get unique results, you could use uniq over the resulting array, but since we are using include, the array you are getting is including the companies too; with a bit of mapping, we can just filter that out.

Employee.find(:all, :include=>:company, :conditions=>"companies.city='Sandusky'").map{|e| e.attributes}.uniq

this way, you are converting the resulting array in an array with only the attributes of Employee model, and now you can just use uniq to remove any dupplicates.

Another approach in order to avoid the mapping would be not using :include, but :join and :select, with the only "problem" that you have to tell ActiveRecord how to join the tables and I would say is a bit less idiomatic because you are in some way breaking the ORM pattern. Also, if you are joining tables through a many to many relationship with another table in the middle, the join could get tricky, so I'd recommed to go with the :include/map solution.

regards,

javier

jramirez@aspgems.com

:include executes the default join, okay. Didn't know that. Does that work if your association is many-to-many using :through?

include makes an outer join with the table described in the association declaration (the has_many or has_one or whatever in the Model). All the supported associations can be included, and since it's an outer join, you always get all the results for your main table and only the matching rows for the included one

Employee.find(:all, :include=>:company,
:conditions=>"companies.city='Sandusky'").map{|e| e.attributes}.uniq

And using map + uniq is something I have not seen before.

Just in case I misled you. Actually uniq has nothing to do with "map". Uniq takes an array and eliminates duplicates. Map takes an array and returns a different array containing whatever we return in the block.

The reason for using map here is that the array returned by find was including both the fields of Employee and Company, so you wouldn't have any duplicates and running uniq would do nothing. What we are doing is using map to get a new array containing only the columns of Employee, so now we can let uniq do its work and clean our list.

regards,

javier ramirez