In_vertical_groups_of

Hi all,

Here’s another contribution for anyone interested.

In Railscast episode 28, Ryan B. 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)

<% @members.in_vertical_groups_of(3) do |group| %> <% group.each do |member| %> <%= render :partial => 'member', :locals => {:member => member} %> <% end %> <% end %>

and then in the _member partial

<%= h "#{member.full_name(false)}" if member %>

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

On Jan 2, 2008 2:10 AM, Phillip K. [email protected] 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.

On Jan 2, 2008, at 5:59 AM, George wrote:

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

end

:slight_smile:

Good grief!

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

On Fri, Jan 04, 2008 at 06:12:15PM +0100, Phillip K. 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

Gregory S. wrote:

On Fri, Jan 04, 2008 at 06:12:15PM +0100, Phillip K. 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

On Fri, Jan 04, 2008 at 08:26:35PM +0100, Phillip K. wrote:

–Greg

n = 2
n = 3
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

George wrote:

On Jan 2, 2008 2:10 AM, Phillip K. [email protected] 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

On Jan 4, 2008, at 1:36 PM, Gregory S. wrote:

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:

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