in_vertical_groups_of

Hi all,

Here's another contribution for anyone interested.

In Railscast episode 28, Ryan Bates showed us in_groups_of which allows you to group array members such as

>> list = [1, 2, 3, 4, 5, 6, 7] => [1, 2, 3, 4, 5, 6, 7] >> list.in_groups_of(3) => [[1, 2, 3], [4, 5, 6], [7, nil, nil]]

This allows you to iterate over the groups and, for example, display a list of things in a tabular format.

1 2 3 4 5 6 7

I thought this was pretty cool, but wanted to go vertically down the column first, instead of horizontally

1 4 7 2 5 3 6

Well, last night I decided I really needed this vertical grouping, so ...

module ActiveSupport   module CoreExtensions     module Array       module Grouping         def in_vertical_groups_of(number, fill_with = nil, &block)           collection = dup           per_column = (collection.size.to_f / number).ceil                      new_collection =                      (0...per_column).each do |i|             (0...number).each do |multiplier|               offset = i + (per_column * multiplier)               new_collection << (offset < collection.size ? collection[offset] : (fill_with != false ? fill_with : nil))             end           end                      return new_collection.in_groups_of(number, fill_with, &block)         end       end     end   end end

All this really does is reorders the array then calls in_groups_of. The only caveat (that I'm aware of right now), is you have to fill "empty" spots in the array or the column spanning won't work right. So in a case where could do this

>> list.in_groups_of(3, false) => [[1, 2, 3], [4, 5, 6], [7]]

in_vertical_groups_of will return

>> list.in_vertical_groups_of(3, false) => [[1, 4, 7], [2, 5, nil], [3, 6, nil]]

The ultimate end of this is

@members = Member.find(:all)

...

<table class="wgg_table">   <% @members.in_vertical_groups_of(3) do |group| %>   <tr style="font-size: 80%">     <% group.each do |member| %>     <%= render :partial => 'member', :locals => {:member => member} %>     <% end %>   </tr>   <% end %> </table>

and then in the _member partial

<td style="width: 125px">   <%= h "#{member.full_name(false)}" if member %> </td>

or whatever your requirements are. This will display a list of members in three columns, top to bottom, left to right, like a phone book.

If anyone discovers something I did poorly or just plain missed, please let me know. Oh, and I have not done any testing/experimenting with passing a block. But since that is just getting passed along, I don't see why it wouldn't work.

Peace, Phillip

Simpler:

  @members.in_groups_of(3).transpose.each do |group|     ...   end

:slight_smile:

Regards, George.

<span id="disgust" style="voice: charlie_brown"> Good grief! </span>

Thanks, George. That is much simpler. I wish I had known about that two days ago. I could have saved myself an hour or two of digging and testing.

:slight_smile:

Peace, Phillip

George wrote:

If anyone discovers something I did poorly or just plain missed, please let me know. Oh, and I have not done any testing/experimenting with passing a block. But since that is just getting passed along, I don't see why it wouldn't work.

Simpler:

  @members.in_groups_of(3).transpose.each do |group|     ...   end

:slight_smile:

Regards, George.

Hi again, George.

As it turns out, transpose is not equivalent to in_vertical_groups_of. To illustrate, open script/console and do this:

list = (1..10).to_a

=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

list.in_groups_of(2)

=> [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

list.in_groups_of(2).transpose

=> [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]

Notice that transpose changed the output from five groups of two to two groups of five. That's not at all what I wanted. It was just coincidence that it happened to work on my particular example. Now, here's the output of in_vertical_groups of:

list.in_vertical_groups_of(2)

=> [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]

They are still in five groups of two, but the sorting is different.

Peace, Phillip

George wrote: >> If anyone discovers something I did poorly or just plain missed, >> please let me know. Oh, and I have not done any testing/experimenting >> with passing a block. But since that is just getting passed along, I >> don't see why it wouldn't work. > > Simpler: > > @members.in_groups_of(3).transpose.each do |group| > ... > end > > :slight_smile: > > Regards, > George.

Hi again, George.

As it turns out, transpose is not equivalent to in_vertical_groups_of. To illustrate, open script/console and do this:

>> list = (1..10).to_a => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >> list.in_groups_of(2) => [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] >> list.in_groups_of(2).transpose => [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]

Notice that transpose changed the output from five groups of two to two groups of five. That's not at all what I wanted. It was just coincidence that it happened to work on my particular example. Now, here's the output of in_vertical_groups of:

>> list.in_vertical_groups_of(2) => [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]

They are still in five groups of two, but the sorting is different.

class Array   def in_vertical_groups_of(n)     in_groups_of(size/n).transpose   end end

Peace, Phillip

--Greg

Gregory Seidman wrote:

> ... To illustrate, open script/console and do this: coincidence that it happened to work on my particular example. Now, here's the output of in_vertical_groups of:

>> list.in_vertical_groups_of(2) => [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]

They are still in five groups of two, but the sorting is different.

class Array   def in_vertical_groups_of(n)     in_groups_of(size/n).transpose   end end

Peace, Phillip

--Greg

Hi Greg,

Wow, that would be nice and concise, if it worked all the time :slight_smile:

list = (1..10).to_a

=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Works here...

n = 2

=> 2

list.in_groups_of(n)

=> [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

list.in_vertical_groups_of(n)

=> [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]

list.in_groups_of(list.size/n).transpose

=> [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]

Doesn't work here...

n = 3

=> 3

list.in_groups_of(n)

=> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, nil, nil]]

list.in_vertical_groups_of(n)

=> [[1, 5, 9], [2, 6, 10], [3, 7, nil], [4, 8, nil]]

list.in_groups_of(list.size/n).transpose

=> [[1, 4, 7, 10], [2, 5, 8, nil], [3, 6, 9, nil]]

nor when n = 4.

As much as I'd like a shorter, cleaner way of doing it, it looks like my method will have to suffice for now. Thanks for trying to slim down my code.

Peace, Phillip

Gregory Seidman wrote: >> > ... >> To illustrate, open script/console and do this: >> coincidence that it happened to work on my particular example. Now, >> here's the output of in_vertical_groups of: >> >> >> list.in_vertical_groups_of(2) >> => [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]] >> >> They are still in five groups of two, but the sorting is different. > > class Array > def in_vertical_groups_of(n) > in_groups_of(size/n).transpose > end > end > >> Peace, >> Phillip > --Greg

Hi Greg,

Wow, that would be nice and concise, if it worked all the time :slight_smile:

>> list = (1..10).to_a => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Works here...

>> n = 2 => 2 >> list.in_groups_of(n) => [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] >> list.in_vertical_groups_of(n) => [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]] >> list.in_groups_of(list.size/n).transpose => [[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]

Doesn't work here...

>> n = 3 => 3 >> list.in_groups_of(n) => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, nil, nil]] >> list.in_vertical_groups_of(n) => [[1, 5, 9], [2, 6, 10], [3, 7, nil], [4, 8, nil]] >> list.in_groups_of(list.size/n).transpose => [[1, 4, 7, 10], [2, 5, 8, nil], [3, 6, 9, nil]]

nor when n = 4.

As much as I'd like a shorter, cleaner way of doing it, it looks like my method will have to suffice for now. Thanks for trying to slim down my code.

Ah, you just didn't give full requirements, i.e. what the output should be if the size did not divide evenly. Try this:

class Array   def in_vertical_groups_of(n)     in_groups_of((size.to_f/n).ceil).transpose   end end

Peace, Phillip

--Greg

Ah, you just didn’t give full requirements, i.e. what the output should be

if the size did not divide evenly. Try this:

In my first message, I said:

This allows you to iterate over the groups and, for example, display

a list of things in a tabular format.

1 2 3

4 5 6

7

I thought this was pretty cool, but wanted to go vertically down the

column first, instead of horizontally

1 4 7

2 5

3 6

Those were pretty much the requirements.

class Array

def in_vertical_groups_of(n)

in_groups_of((size.to_f/n).ceil).transpose

end

end

This does, in fact, work. Thanks. I’ll replace my looping code with someone else’s looping code :slight_smile:

For anyone following along, you might want to now replace my in_vertical_groups_of with this new one:

def in_vertical_groups_of(number, fill_with = nil, &block)

return in_groups_of((size.to_f / number).ceil, fill_with, &block).transpose

end

And then send Greg a +1 email!

Peace,

Phillip

–Greg

Until my next poorly contrived contribution to the community…

Phillip