“数组#each_slice”,在开始时留下余数

时间:2022-05-28 01:28:37

I'm trying to slice an array into groups of three. I want to have the remainders at the beginning ([1, 2] in the following example).

我想把一个数组分成三组。我希望在后面的例子中有余数([1,2])。

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
...
#=> [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

Is there a nifty way to do this?

有什么好办法吗?

The usual way to split an array would be:

通常分割数组的方法是:

arr.each_slice(3)
# => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11]]

This gives the remainders [10, 11] at the end. I tried as below, thinking each_slice might accept a negative argument and read it as going backwards through the array.

这就给出了最后的余数[10,11]。我尝试如下所示,认为each_slice可能接受一个否定的参数,并将其作为反向遍历数组。

arr.each_slice(-3)

Alas, it didn't work.

唉,没有工作。

2 个解决方案

#1


9  

Though one might reverse an array thrice, there is more efficient way to achieve a goal:

虽然我们可以三次反转数组,但是有更有效的方法来实现目标:

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
a = arr.dup # not to modify it inplace
[a.shift(a.size % 3)] + a.each_slice(3).to_a
#⇒ [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

BTW, arr.each_slice(3) returns an enumerator, not an array as you posted in the question.

顺便说一下,arr.each_slice(3)返回的是一个枚举数,而不是问题中提到的数组。

upd or, as suggested by Cary Swoveland, to void dup:

upd或,如Cary Swoveland所建议的,取消dup:

n = a.size % 3
[a[0...n]] + a[n..-1].each_slice(3).to_a

upd getting rid of dup by @sawa:

upd摆脱dup by @sawa:

[a.first(a.size % 3)] + a.drop(a.size % 3).each_slice(3).to_a

upd just out of curiosity (assuming an input has no nil elements):

upd只是出于好奇(假设输入没有nil元素):

([nil] * (3 - a.size % 3) + a).each_slice(3).to_a.tap do |a|
  a.unshift(a.shift.compact!)
end

the above might be safely run on original array, it does not modify it inplace.

上面的操作可以安全地在原始数组上运行,它不会对其进行就地修改。

UPD2 as pointed out by Stefan in comments, any of the above will produce an initial empty slice if the array is divisible by 3. So, the proper solution (and, the fastest, btw) should look like:

正如Stefan在评论中指出的,如果数组能被3整除,上面的任何一个都将产生一个初始的空片。因此,合适的解决方案(顺便说一句,最快的解决方案)应该是:

(arr.size % 3).zero? ? arr.each_slice(3).to_a : ANY_OF_THE_ABOVE

#2


7  

A simple combination of reverse and each_slice will do the trick:

一个简单的反向和each_slice的组合就可以做到:

arr.reverse.each_slice(3).map(&:reverse).reverse
#=> [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

Explanation:

解释:

I am reversing the Array and doing each_slice. That will give me:

我正在反转数组并执行each_slice。会给我:

[[11, 10, 9], [8, 7, 6], [5, 4, 3], [2, 1]]

Now I'm iterating over this and reversing each sub-arrays to match the expected sub-array sequence with .map(&:reverse). At last I am reversing the whole Array to get the desired sequence (with last .reverse).

现在,我正在迭代这一过程,并反转每个子数组,以匹配期望的子数组序列与.map(&:反向)。最后,我反转整个数组以得到所需的序列(最后一个。reverse)。

#1


9  

Though one might reverse an array thrice, there is more efficient way to achieve a goal:

虽然我们可以三次反转数组,但是有更有效的方法来实现目标:

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
a = arr.dup # not to modify it inplace
[a.shift(a.size % 3)] + a.each_slice(3).to_a
#⇒ [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

BTW, arr.each_slice(3) returns an enumerator, not an array as you posted in the question.

顺便说一下,arr.each_slice(3)返回的是一个枚举数,而不是问题中提到的数组。

upd or, as suggested by Cary Swoveland, to void dup:

upd或,如Cary Swoveland所建议的,取消dup:

n = a.size % 3
[a[0...n]] + a[n..-1].each_slice(3).to_a

upd getting rid of dup by @sawa:

upd摆脱dup by @sawa:

[a.first(a.size % 3)] + a.drop(a.size % 3).each_slice(3).to_a

upd just out of curiosity (assuming an input has no nil elements):

upd只是出于好奇(假设输入没有nil元素):

([nil] * (3 - a.size % 3) + a).each_slice(3).to_a.tap do |a|
  a.unshift(a.shift.compact!)
end

the above might be safely run on original array, it does not modify it inplace.

上面的操作可以安全地在原始数组上运行,它不会对其进行就地修改。

UPD2 as pointed out by Stefan in comments, any of the above will produce an initial empty slice if the array is divisible by 3. So, the proper solution (and, the fastest, btw) should look like:

正如Stefan在评论中指出的,如果数组能被3整除,上面的任何一个都将产生一个初始的空片。因此,合适的解决方案(顺便说一句,最快的解决方案)应该是:

(arr.size % 3).zero? ? arr.each_slice(3).to_a : ANY_OF_THE_ABOVE

#2


7  

A simple combination of reverse and each_slice will do the trick:

一个简单的反向和each_slice的组合就可以做到:

arr.reverse.each_slice(3).map(&:reverse).reverse
#=> [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

Explanation:

解释:

I am reversing the Array and doing each_slice. That will give me:

我正在反转数组并执行each_slice。会给我:

[[11, 10, 9], [8, 7, 6], [5, 4, 3], [2, 1]]

Now I'm iterating over this and reversing each sub-arrays to match the expected sub-array sequence with .map(&:reverse). At last I am reversing the whole Array to get the desired sequence (with last .reverse).

现在,我正在迭代这一过程,并反转每个子数组,以匹配期望的子数组序列与.map(&:反向)。最后,我反转整个数组以得到所需的序列(最后一个。reverse)。