views:

1381

answers:

4

I have a two tables joined with a join table - this is just pseudo code:

Library
Book
LibraryBooks

What I need to do is if i have the id of a library, i want to get all the libraries that all the books that this library has are in.

So if i have Library 1, and Library 1 has books A and B in them, and books A and B are in Libraries 1, 2, and 3, is there an elegant (one line) way todo this in rails?

I was thinking:

l = Library.find(1)
allLibraries = l.books.libraries

But that doesn't seem to work. Suggestions?

+3  A: 

Perhaps:

l.books.map {|b| b.libraries}

or

l.books.map {|b| b.libraries}.flatten.uniq

if you want it all in a flat array.

Of course, you should really define this as a method on Library, so as to uphold the noble cause of encapsulation.

Jim Puls
+2  A: 

If you want a one-dimensional array of libraries returned, with duplicates removed.

l.books.map{|b| b.libraries}.flatten.uniq
brady8
+2  A: 

One problem with

l.books.map{|b| b.libraries}.flatten.uniq

is that it will generate one SQL call for each book in l. A better approach (assuming I understand your schema) might be:

LibraryBook.find(:all, :conditions => ['book_id IN (?)', l.book_ids]).map(&:library_id).uniq
Ben Scofield
This isn't strictly true, depending on what has been loaded initially.
Jim Puls
sorry, I should have made that clear: I'm assuming you haven't preloaded anything other than l (the initial library)
Ben Scofield
oops... not sure this works ... did you mean LibraryBook or Library?
aronchick
+4  A: 
l = Library.find(:all, :include => :books)
l.books.map { |b| b.library_ids }.flatten.uniq

Note that map(&:library_ids) is slower than map { |b| b.library_ids } in Ruby 1.8.6, and faster in 1.9.0.

I should also mention that if you used :joins instead of include there, it would find the library and related books all in the same query speeding up the database time. :joins will only work however if a library has books.

Ryan Bigg
the slowness of Symbol#to_proc is usually outweighed by the database calls.
James Deville
I do not see how this could work. The first line will return an array of Library objects (well actually a proxy, but with the same methods). This array will not have a "books" method, so line two will fail, won't it?
MiniQuark