One Array - 3 Columns of Equal Length

I want to split a single array into 3 columns of equal (or as near to as
possible) length. What would you advise I do?

http://weblog.rubyonrails.org/2006/3/1/new-in-rails-enumerable-group_by-and-array-in_groups_of

enjoy!

http://weblog.rubyonrails.org/2006/3/1/new-in-rails-enumerable-group_by-and-array-in_groups_of

enjoy!

This isn’t quite the same thing though.

a = %w(1 2 3 4 5 6 7 8 9 10)

a.in_groups_of(3)

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

It gives you 4 groups, not three as the OP’s expecting.

I would use the Array#chunk method at http://blog.jayfields.com/2007/09/ruby-arraychunk.html like this:

a.chunk(3)

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

Cheers,

Andy

chewmanfoo wrote:

http://weblog.rubyonrails.org/2006/3/1/new-in-rails-enumerable-group_by-and-array-in_groups_of

enjoy!

Thank you for your quick response. Sadly, I can't get this array method
to do what I wish. See below:

<% Category.all.in_groups_of(3, false) do |sorted_categories| %> |
<% for category in sorted_categories %>
  <%= link_to category.name, :action => 'list', :category => category %>
<% end %>
<% end %>

The above is the code I'm using.

It currently sorts my categories as below:

> Category1 Category2 Category3 | Category4 Category5 Category6 |

This is not what I want. What I want is:

I want to split a single array into 3 columns of equal (or as near to as
possible) length. What would you advise I do?

Well, that depends on whether you want the elements to flow row-wise or column-wise.

require 'enumerator'

=> true

array = %w[ a b c d e f g h i j k l m n ]

=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n"]

## Row-wise is easy

array.each_slice(3) do |a,b,c|

        puts [a,b,c]*' '
      end
a b c
d e f
g h i
j k l
m n
=> nil

## Column-wise is a bit more work

row_count = (array.length + (3-1))/3

=> 5

columns = []

=> []

array.each_slice(row_count) do |col|

        columns << col
        columns[-1] << nil while columns[-1].length < row_count
      end
=> nil

columns.transpose.each do |a,b,c|

        puts [a,b,c]*' '
      end
a f k
b g l
c h m
d i n
e j
=> [["a", "f", "k"], ["b", "g", "l"], ["c", "h", "m"], ["d", "i", "n"], ["e", "j", nil]]

Enjoy!

-Rob

Rob Biedenharn
http://agileconsultingllc.com
  Rob@AgileConsultingLLC.com
http://gaslightsoftware.com
  rab@GaslightSoftware.com

That looks a little clunky to me... I'd rather use

  (array.length / 3.0).ceil

No difference in the result though! :slight_smile:

But the OP's request is a little vague as people have pointed out -
Should the three groups be 'shuffled' (ie one of each of the first
three elements in each column, and one of each of the next three, and
so on...)? or is it the 1st, 2nd, and 3rd third of elements grouped
together? is it three arrays of equal size for use in the controller?
or three columns in a view that's needed? and if in the view, is it
data that can be presented in an HTML table, or will it be CSS floated
layout elements?

Hopefully though, there's been enough suggestions and pointers to the
API docs for Enumerable for the OP to get a result :slight_smile:

[...]

## Column-wise is a bit more work

> row_count = (array.length + (3-1))/3
=> 5
> columns = []
=> []

[...]

Why go to all that trouble? in_groups and zip. Done. No arithmetic.

Best,

in_groups and in_groups_of

part of rails api - hadn't spotted those before and would never have
thought of looking for them.
I often wonder how many other useful little goodies I may be missing.
Always useful to keep an eye on this group.

thanks Marnen

The have a page through the API docs... it's all in there!

There's interesting stuff in both the Ruby [1] and Rails [2] APIs.
You'll learn loads from looking through both resources' methods for
String, Integer, Array, Enumerable; and in Rails, the
ActionView::Helpers are great eye openers.

[1] http://ruby-doc.org/ - look at the "core api" link for your version
[2] http://api.rubyonrails.org/

Marnen Laibow-Koser wrote:

[...]

## Column-wise is a bit more work

> row_count = (array.length + (3-1))/3
=> 5
> columns = []
=> []

[...]

Why go to all that trouble? in_groups and zip. Done. No arithmetic.

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

That really depends on what the desired output is. The OP indicated that
he was looking for a phone book style sorting, top-to-bottom,
left-to-right.

It currently sorts my categories as below:

> Category1 Category2 Category3 | Category4 Category5 Category6 |

This is not what I want. What I want is:

> Category1 | Category4 |
> Category2 | Category5 |
> Category3 | Category6 |

in_groups doesn't do exactly that. Here is an example:

Given a = [1,2,3,4,5,6,7,8,9] and I want the output to be

1 6
2 7
3 8
4 9
5

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

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

a.in_groups(2)

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

a.in_groups_of(2)

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

Here is a method that was constructed back in the Rails 1.2 days [1]:

def in_vertical_groups_of(number, fill_with = nil, &block)
  return in_groups_of((size.to_f / number).ceil, fill_with,
&block).transpose
end

a.in_vertical_groups_of(2)

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

I add this to something that is in config/intializers

module ActiveSupport
  module CoreExtensions
    module Array
      module Grouping
        def in_vertical_groups_of
        end
      end
    end
  end
end

Peace,
Phillip

[1] original thread: http://www.ruby-forum.com/topic/137230

Phillip Koebbe wrote:

Marnen Laibow-Koser wrote:

[...]

## Column-wise is a bit more work

> row_count = (array.length + (3-1))/3
=> 5
> columns = []
=> []

[...]

Why go to all that trouble? in_groups and zip. Done. No arithmetic.

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

That really depends on what the desired output is.

You're right. I assumed the OP wanted the output he said he wanted. :slight_smile:

The OP indicated that
he was looking for a phone book style sorting, top-to-bottom,
left-to-right.

I know that.

It currently sorts my categories as below:

> Category1 Category2 Category3 | Category4 Category5 Category6 |

This is not what I want. What I want is:

> Category1 | Category4 |
> Category2 | Category5 |
> Category3 | Category6 |

in_groups doesn't do exactly that.

I'm aware of that. That's why I suggested in_groups *and zip*, which
will do exactly what the OP wanted.

Please don't only read half of my solution and claim I'm wrong because
you missed the rest.

Best,

Marnen Laibow-Koser wrote:

Phillip Koebbe wrote:

Marnen Laibow-Koser wrote:

[...]

## Column-wise is a bit more work

> row_count = (array.length + (3-1))/3
=> 5
> columns = []
=> []

[...]

Why go to all that trouble? in_groups and zip. Done. No arithmetic.

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

I'm aware of that. That's why I suggested in_groups *and zip*, which
will do exactly what the OP wanted.

Please don't only read half of my solution and claim I'm wrong because
you missed the rest.

I didn't realize "zip" was a reference to a method call. I actually
thought you were making a reference to something speedy. You know, like
"Zip, you're done." Since "zip" was followed by "Done", who was I to
question your placement of a period. For that oversight, I'm terribly
sorry.

Relax.

Peace,
Phillip

Phillip Koebbe wrote:

Marnen Laibow-Koser wrote:

[...]

## Column-wise is a bit more work

> row_count = (array.length + (3-1))/3
=> 5
> columns = []
=> []

[...]

Why go to all that trouble? in_groups and zip. Done. No arithmetic.

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

That really depends on what the desired output is.

You're right. I assumed the OP wanted the output he said he wanted. :slight_smile:

The OP indicated that
he was looking for a phone book style sorting, top-to-bottom,
left-to-right.

I know that.

It currently sorts my categories as below:

> Category1 Category2 Category3 | Category4 Category5 Category6 |

This is not what I want. What I want is:

> Category1 | Category4 |
> Category2 | Category5 |
> Category3 | Category6 |

in_groups doesn't do exactly that.

I'm aware of that. That's why I suggested in_groups *and zip*, which
will do exactly what the OP wanted.

Please don't only read half of my solution and claim I'm wrong because
you missed the rest.

It would be so much less confusing if you would answer in actual *code* rather than just spewing documentation.

-Rob

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

Sent from my iPhone
--
Posted via http://www.ruby-forum.com/.

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
To post to this group, send email to rubyonrails-talk@googlegroups.com.
To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.

Rob Biedenharn
Rob@AgileConsultingLLC.com http://AgileConsultingLLC.com/
rab@GaslightSoftware.com http://GaslightSoftware.com/

Phillip Koebbe wrote:
[...]

I didn't realize "zip" was a reference to a method call. I actually
thought you were making a reference to something speedy. You know, like
"Zip, you're done." Since "zip" was followed by "Done", who was I to
question your placement of a period. For that oversight, I'm terribly
sorry.

Oh, now I understand the confusion. Yeah, that would do it. :slight_smile:

Relax.

Peace,
Phillip

Best,

Rob Biedenharn wrote:

It currently sorts my categories as below:

I'm aware of that. That's why I suggested in_groups *and zip*, which
will do exactly what the OP wanted.

Please don't only read half of my solution and claim I'm wrong because
you missed the rest.

It would be so much less confusing if you would answer in actual
*code* rather than just spewing documentation.

I told the OP which methods would do the job for him. I didn't "spew
documentation" as I understand that phrase. I didn't write code,
either, because I generally don't like to do that for others on the list
(as I do not expect others to do it for me), unless the solution is
fairly complex and/or unintuitive. In this case, I don't really think
it was.

-Rob

Best,

Your response was quoting a bit of code to which you said "Why go to all that trouble?" and then mentioned two methods. I think it would have been better to demonstrate how those two methods would avoid the "trouble" than to have simply tossed them out.

-Rob

Rob Biedenharn
Rob@AgileConsultingLLC.com http://AgileConsultingLLC.com/
rab@GaslightSoftware.com http://GaslightSoftware.com/

Rob Biedenharn wrote:

Please don't only read half of my solution and claim I'm wrong

(as I do not expect others to do it for me), unless the solution is
marnen@marnen.org

Your response was quoting a bit of code to which you said "Why go to
all that trouble?" and then mentioned two methods. I think it would
have been better to demonstrate how those two methods would avoid the
"trouble" than to have simply tossed them out.

Again, I'm sorry if it was confusing. I think I was pressed for time
when I responded, and anyhow, there are only a couple of ways one could
combine the two methods, so I didn't think further explanation was
necessary. Perhaps I misjudged it.

-Rob

Best,