70

我需要一种方法将数组拆分为大小大致相等的较小数组的确切数量。任何人都有这样做的方法吗?

例如

a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] 
groups = a.method_i_need(3)
groups.inspect
    => [[1,2,3,4,5], [6,7,8,9], [10,11,12,13]]

请注意,这是一个与将数组划分为 chunks完全不同的问题,因为a.each_slice(3).to_a会产生 5 个组(不是我们想要的 3 个组),并且最终组的大小可能与其他组完全不同:

[[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13]]  # this is NOT desired here.

在这个问题中,需要预先指定所需的 chunk 数量,每个 chunk 的大小最多相差 1。

4

5 回答 5

131

您正在寻找Enumerable#each_slice

a = [0, 1, 2, 3, 4, 5, 6, 7]
a.each_slice(3) # => #<Enumerator: [0, 1, 2, 3, 4, 5, 6, 7]:each_slice(3)>
a.each_slice(3).to_a # => [[0, 1, 2], [3, 4, 5], [6, 7]]
于 2012-09-11T17:12:03.400 回答
122

也许我误读了这个问题,因为另一个答案已经被接受,但听起来你想将数组分成 3 个相等的组,而不考虑每个组的大小,而不是将它分成 3 个的 N 组作为以前的答案可以。如果这就是您要寻找的,Rails (ActiveSupport) 也有一个名为in_groups的方法:

a = [0,1,2,3,4,5,6]
a.in_groups(2) # => [[0,1,2,3],[4,5,6,nil]]
a.in_groups(3, false) # => [[0,1,2],[3,4], [5,6]]

我认为没有 ruby​​ 等价物,但是,您可以通过添加这个简单的方法获得大致相同的结果:

class Array; def in_groups(num_groups)
  return [] if num_groups == 0
  slice_size = (self.size/Float(num_groups)).ceil
  groups = self.each_slice(slice_size).to_a
end; end

a.in_groups(3) # => [[0,1,2], [3,4,5], [6]]

唯一的区别(如您所见)是这不会将“空白空间”分散到所有组中;除了最后一个之外,每个组的大小都相同,最后一个组始终包含余数加上所有“空白空间”。

更新: 正如@rimsky 敏锐地指出的那样,上述方法并不总是会产生正确数量的组(有时它会在最后创建多个“空组”,并将它们排除在外)。这是一个更新版本,从ActiveSupport 的定义中缩减,该定义将附加内容展开以填充请求的组数。

def in_groups(number)
  group_size = size / number
  leftovers = size % number

  groups = []
  start = 0
  number.times do |index|
    length = group_size + (leftovers > 0 && leftovers > index ? 1 : 0)
    groups << slice(start, length)
    start += length
  end

  groups
end
于 2012-11-29T20:47:48.280 回答
16

尝试

a.in_groups_of(3,false)

它会做你的工作

于 2012-09-11T17:12:55.897 回答
5

正如 mltsy 所写,in_groups(n, false)应该完成这项工作。

我只是想添加一个小技巧来获得正确的平衡 my_array.in_group(my_array.size.quo(max_size).ceil, false)

下面是一个例子来说明这个技巧:

a = (0..8).to_a
a.in_groups(4, false) => [[0, 1, 2], [3, 4], [5, 6], [7, 8]]
a.in_groups(a.size.quo(4).ceil, false) => [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
于 2017-03-10T14:47:38.853 回答
3

这需要一些更好的聪明才智来涂抹多余的部分,但这是一个合理的开始。

def i_need(bits, r)
  c = r.count
  (1..bits - 1).map { |i| r.shift((c + i) * 1.0 / bits ) } + [r]
end

>   i_need(2, [1, 3, 5, 7, 2, 4, 6, 8])
 => [[1, 3, 5, 7], [2, 4, 6, 8]] 
> i_need(3, [1, 3, 5, 7, 2, 4, 6, 8])
 => [[1, 3, 5], [7, 2, 4], [6, 8]] 
> i_need(5, [1, 3, 5, 7, 2, 4, 6, 8])
 => [[1, 3], [5, 7], [2, 4], [6], [8]] 
于 2017-11-06T09:41:57.617 回答