Reputation: 34113
I have five categories with about five posts each. E.g.
=> category_a.posts
=> #<Post id:01>, #<Post id:02>, #<Post id:03> etc
=> category_b.posts
=> #<Post id:10>, #<Post id:11>, #<Post id:12> etc
=> category_c.posts
=> #<Post id:20>, #<Post id:21>, #<Post id:22> etc
I'd like a collection of children where they are interleaved (as evenly as possible), i.e.
#<Post id:01>, #<Post id:10>, #<Post id:20>, #<Post id:02>, #<Post id:11> etc
How could I accomplish this?
Upvotes: 1
Views: 91
Reputation: 417
The answer by Dave Schweisguth is ok and probably lets you know what you need to know to solve the problem. But, that solution does not let you choose the order the items get interleaved in. Here is a, hopefully, more general solution:
def full_zip(*args)
max_len = args.map(&:length).max
([nil] * max_len).zip(*args).flatten.compact
end
and then this would be used like full_zip(category_a.posts, category_b.posts, category_c.posts)
or full_zip(*[cat_a, cat_z].map(&:posts))
. Use the categories in whatever order you want them in.
Edit: one problem with this one is if the original lists have nils in them you want to keep.
Upvotes: 2
Reputation: 37657
It's only five queries and around 15 models, so it would be fine to do it in memory:
posts =
[category_a, category_b, category_c, category_d, category_e].
map(&:posts)
sort_by(&:length)
posts.first.zip(*posts[1..-1]).flatten.compact
Putting the longest array first prevents zip
from dropping elements from the arrays in its arguments that are longer than the array it's called on. compact
removes any nil
s created by zip
if the arrays in its arguments are longer than the array it's called on.
Upvotes: 1