-1

Let's say I want to get a certain number of even groups based on a collection of records with varying count. How is this possible?

I'm looking for a method like objects.in_x_even_groups(4)

Abram
  • 39,950
  • 26
  • 134
  • 184
  • 1
    Please define "even groups". If you have 7 items and you want 3 groups, for example, do you want 3-3-1, 3-2-2 or something else? Also, are they to be kept in order? – Cary Swoveland May 23 '19 at 17:16
  • Yes, I should have specified the ordering. See @Max's answer below. He got close to what I was looking for... – Abram May 23 '19 at 18:39
  • Your comment does not help. Please edit to state precisely what you want to do. – Cary Swoveland May 23 '19 at 20:08
  • I was specifically looking for the output from `in_groups(3, false)` ... Ordered groups within even(ish) columns. – Abram May 23 '19 at 21:07

4 Answers4

2

Group your objects by their index modulo the number of groups.

objects.group_by.with_index { |_, i| i % num_groups }.values

Example:

objects = %w{a b c d e f g h i j k}
objects.group_by.with_index { |_, i| i % 3 }.values
# [["a", "d", "g", "j"], ["b", "e", "h", "k"], ["c", "f", "i"]]

This won't pad undersized groups with nil and it also will interleave your objects. So this won't work if you need consecutive objects to be in the same group.

Max
  • 21,123
  • 5
  • 49
  • 71
  • Good answer, and happy to give you credit, but I found an easier way that handles vertical ordering per row, if you want to add that to your answer too, I will check your answer as correct `objects.in_groups(3, false)` <-- the "false" removes nil objects. – Abram May 23 '19 at 18:38
  • @Abram you should mark one of the `in_groups` answers as correct. I don't mind – Max May 24 '19 at 16:14
1

You are probably looking for the in_groups method. From the docs:

in_groups(number, fill_with = nil)

Splits or iterates over the array in number of groups, padding any remaining slots with fill_with unless it is false.

%w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
["1", "2", "3", "4"]
["5", "6", "7", nil]
["8", "9", "10", nil]
spickermann
  • 100,941
  • 9
  • 101
  • 131
  • This is rails only – Max May 23 '19 at 16:41
  • That is my fault. I asked for ruby, but rails worked for me. I upvoted this one, but I'll give Cary the check/tick since he actually answered the question I asked. – Abram May 24 '19 at 21:00
0

I assume:

  • the elements are to be kept in order;
  • l-s is to be minimized, where l is the size of the largest group and s is the size of the smallest group; and
  • group sizes are non-increasing.

l-s will be at most 1.

def group_em(arr, ngroups)
  n_per_group, left_over = arr.size.divmod(ngroups)
  cum_off = 0
  ngroups.times.map do |i|
    n = n_per_group + (i < left_over ? 1 : 0)
    a = arr[cum_off, n]
    cum_off += n
    a     
  end
end

arr = [1, 2, 3, 4, 5, 6, 7]
(1..7).each { |m| puts "ngroups=#{m}: #{group_em(arr, m)}" }
ngroups=1: [[1, 2, 3, 4, 5, 6, 7]]
ngroups=2: [[1, 2, 3, 4], [5, 6, 7]]
ngroups=3: [[1, 2, 3], [4, 5], [6, 7]]
ngroups=4: [[1, 2], [3, 4], [5, 6], [7]]
ngroups=5: [[1, 2], [3, 4], [5], [6], [7]]
ngroups=6: [[1, 2], [3], [4], [5], [6], [7]]
ngroups=7: [[1], [2], [3], [4], [5], [6], [7]]
Community
  • 1
  • 1
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
-1

You're looking for in_groups_of:

https://apidock.com/rails/Array/in_groups_of

array = %w(1 2 3 4 5 6 7 8 9 10)
array.in_groups_of(3) {|group| p group}
=>
["1", "2", "3"]
["4", "5", "6"]
["7", "8", "9"]
["10", nil, nil]
Mark
  • 6,112
  • 4
  • 21
  • 46