Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Array grouping methods with index #556

Closed
wants to merge 2 commits into from

3 participants

@xuanxu

A couple of utility methods to port the functionality of Array#each_with_index to the grouping methods.

@josevalim
Owner

Could you please send this to the mailing list for discussion about inclusion or not?

@NZKoz NZKoz closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
87 activesupport/lib/active_support/core_ext/array/grouping.rb
@@ -35,7 +35,47 @@ def in_groups_of(number, fill_with = nil)
groups
end
end
+
+ # Splits or iterates over the array with an index (which start from 0)
+ # in groups of size +number+, padding any remaining slots
+ # with +fill_with+ unless it is +false+. If no block is given returns
+ # an array of [group, index] arrays.
+ #
+ # %w(1 2 3 4 5 6 7).with_index_in_groups_of(3) {|group, index| p "#{group} : #{index}"}
+ # ["1", "2", "3"] : 0
+ # ["4", "5", "6"] : 1
+ # ["7", nil, nil] : 2
+ #
+ # %w(1 2 3).with_index_in_groups_of(2, ' ') {|group, index| p "#{group} : #{index}"}
+ # ["1", "2"] : 0
+ # ["3", " "] : 1
+ #
+ # %w(1 2 3).with_index_in_groups_of(2, false) {|group, index| p "#{group} : #{index}"}
+ # ["1", "2"] : 0
+ # ["3"] : 1
+ #
+ # %w(1 2 3).with_index_in_groups_of(2) #=> [ [["1", "2"], 0], [["3", nil, nil], 1] ]
+ def with_index_in_groups_of(number, fill_with = nil)
+ if fill_with == false
+ collection = self
+ else
+ # size % number gives how many extra we have;
+ # subtracting from number gives how many to add;
+ # modulo number ensures we don't add group of just fill.
+ padding = (number - size % number) % number
+ collection = dup.concat([fill_with] * padding)
+ end
+ groups = []
+ collection.each_slice(number) { |group| groups << group }
+
+ if block_given?
+ groups.each_with_index { |slice, index| yield(slice, index) }
+ else
+ groups.each_with_index.to_a
+ end
+ end
+
# Splits or iterates over the array in +number+ of groups, padding any
# remaining slots with +fill_with+ unless it is +false+.
#
@@ -78,6 +118,53 @@ def in_groups(number, fill_with = nil)
groups
end
end
+
+ # Splits or iterates over the array in +number+ of groups with an index
+ # (which start from 0), padding any remaining slots with +fill_with+
+ # unless it is +false+. If no block is given returns an array of
+ # +number+ [group, index] arrays.
+ #
+ # %w(1 2 3 4 5 6 7 8 9 10).with_index_in_groups(3) {|group, index| p "#{group} : #{index}"}
+ # ["1", "2", "3", "4"] : 0
+ # ["5", "6", "7", nil] : 1
+ # ["8", "9", "10", nil] : 2
+ #
+ # %w(1 2 3 4 5 6 7).with_index_in_groups(3, '&nbsp;') {|group, index| p "#{group} : #{index}"}
+ # ["1", "2", "3"] : 0
+ # ["4", "5", "&nbsp;"] : 1
+ # ["6", "7", "&nbsp;"] : 2
+ #
+ # %w(1 2 3 4 5 6 7).with_index_in_groups(3, false) {|group, index| p "#{group} : #{index}"}
+ # ["1", "2", "3"] : 0
+ # ["4", "5"] : 1
+ # ["6", "7"] : 2
+ #
+ # %w(1 2 3).with_index_in_groups(2, false) #=> [ [["1", "2"], 0], [["3"], 1] ]
+ def with_index_in_groups(number, fill_with = nil)
+ # size / number gives minor group size;
+ # size % number gives how many objects need extra accommodation;
+ # each group hold either division or division + 1 items.
+ division = size / number
+ modulo = size % number
+
+ # create a new array avoiding dup
+ groups = []
+ start = 0
+
+ number.times do |index|
+ length = division + (modulo > 0 && modulo > index ? 1 : 0)
+ padding = fill_with != false &&
+ modulo > 0 && length == division ? 1 : 0
+ groups << slice(start, length).concat([fill_with] * padding)
+ start += length
+ end
+
+ if block_given?
+ groups.each_with_index { |g, i| yield(g, i) }
+ else
+ groups.each_with_index.to_a
+ end
+ end
# Divides the array into one or more subarrays based on a delimiting +value+
# or the result of an optional block.
View
92 activesupport/test/core_ext/array_ext_test.rb
@@ -118,6 +118,7 @@ def test_in_groups_of_with_perfect_fit
def test_in_groups_of_with_padding
groups = []
+
('a'..'g').to_a.in_groups_of(3) do |group|
groups << group
end
@@ -144,6 +145,55 @@ def test_in_groups_of_without_padding
assert_equal [%w(a b c), %w(d e f), ['g']], groups
end
+
+ def test_with_index_in_groups_of_with_perfect_fit
+ groups = {}
+
+ ('a'..'i').to_a.with_index_in_groups_of(3) do |group, index|
+ groups[index] = group
+ end
+
+ indexed = {0 => %w(a b c), 1 => %w(d e f), 2 => %w(g h i)}
+
+ assert_equal indexed, groups
+ assert_equal [[%w(a b c), 0], [%w(d e f), 1], [%w(g h i), 2]], ('a'..'i').to_a.with_index_in_groups_of(3)
+ end
+
+ def test_with_index_in_groups_of_with_padding
+ groups = {}
+
+ ('a'..'g').to_a.with_index_in_groups_of(3) do |group, index|
+ groups[index] = group
+ end
+
+ indexed = {0 => %w(a b c), 1 => %w(d e f), 2 => ['g', nil, nil]}
+
+ assert_equal indexed, groups
+ end
+
+ def test_with_index_in_groups_of_pads_with_specified_values
+ groups = {}
+
+ ('a'..'g').to_a.with_index_in_groups_of(3, 'foo') do |group, index|
+ groups[index] = group
+ end
+
+ indexed = {0 => %w(a b c), 1 => %w(d e f), 2 => ['g', 'foo', 'foo']}
+
+ assert_equal indexed, groups
+ end
+
+ def test_with_index_in_groups_of_without_padding
+ groups = {}
+
+ ('a'..'g').to_a.with_index_in_groups_of(3, false) do |group, index|
+ groups[index] = group
+ end
+
+ indexed = {0 => %w(a b c), 1 => %w(d e f), 2 => ['g']}
+
+ assert_equal indexed, groups
+ end
def test_in_groups_returned_array_size
array = (1..7).to_a
@@ -186,6 +236,48 @@ def test_in_groups_without_padding
assert_equal [[1, 2, 3], [4, 5], [6, 7]],
(1..7).to_a.in_groups(3, false)
end
+
+ def test_with_index_in_groups_returned_array_size
+ array = (1..7).to_a
+
+ 1.upto(array.size + 1) do |number|
+ assert_equal number, array.with_index_in_groups(number).size
+ end
+ end
+
+ def test_with_index_in_groups_with_empty_array
+ assert_equal [[[], 0], [[],1], [[],2]], [].with_index_in_groups(3)
+ end
+
+ def test_with_index_in_groups_with_block
+ array = (1..9).to_a
+ groups = []
+
+ array.with_index_in_groups(3) do |group, index|
+ groups << [group, index]
+ end
+
+ assert_equal array.with_index_in_groups(3), groups
+ end
+
+ def test_with_index_in_groups_with_perfect_fit
+ assert_equal [[[1, 2, 3], 0], [[4, 5, 6], 1], [[7, 8, 9], 2]],
+ (1..9).to_a.with_index_in_groups(3)
+ end
+
+ def test_with_index_in_groups_with_padding
+ array = (1..7).to_a
+
+ assert_equal [[[1, 2, 3], 0], [[4, 5, nil], 1], [[6, 7, nil], 2]],
+ array.with_index_in_groups(3)
+ assert_equal [[[1, 2, 3], 0], [[4, 5, 'foo'], 1], [[6, 7, 'foo'], 2]],
+ array.with_index_in_groups(3, 'foo')
+ end
+
+ def test_with_index_in_groups_without_padding
+ assert_equal [[[1, 2, 3], 0], [[4, 5], 1], [[6, 7], 2]],
+ (1..7).to_a.with_index_in_groups(3, false)
+ end
end
class ArraySplitTests < Test::Unit::TestCase
Something went wrong with that request. Please try again.