我有一个数组:
["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
我想转换为:
[["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", false, false]]
尝试使用each_slice
但返回不带false
值的子数组:
array = ["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
array.each_slice(3).to_a
#=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7"]]
任何支持所需行为的 Ruby 方法?
#tap
到生成的数组中,并#fill
具有必要数量的false
元素的最后一个元素:
array = ["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
array
.each_slice(3)
.to_a
.tap { |a| a.last.fill(false, a.last.length, 3 - a.last.length) }
您可以使用以下命令计算所需的填充元素的数量:
-array.size % chunk_size
通过链接枚举器,可以像 iGian 的答案一样执行填充,而无需创建中间数组或接触原始数组:
> [1, 2, 3] + [4]
=> [1, 2, 3, 4]
> [1,2,3].each + [4]
=> #<Enumerator::Chain: ...>
> ([1,2,3].each + [4]).to_a
=> [1, 2, 3, 4]
用它制作一个方法:
def padded_each_slice(array, chunk_size = 3, pad = false)
(array.each + Array.new(-array.size % chunk_size, pad)).each_slice(chunk_size)
end
让我们对其进行测试:
> array = ["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
> padded_each_slice(array).to_a
=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", false, false]]
> array << '1'
> padded_each_slice(array).to_a
=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", "1", false]]
> array << '2'
> padded_each_slice(array).to_a
=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", "1", "2"]]
> array << '3'
> padded_each_slice(array).to_a
=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", "1", "2"], ["3", false, false]]
也许不是那么漂亮,但想添加一个带有链式枚举器的答案,这是 Ruby 2.6 中引入的一项新功能。
包括@Stefan在评论中建议的改进。
n = 3
arr = ["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
arr.each_slice(n).map { |a| n.times.map { |i| a.fetch(i, false) } }
#=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", false, false]]
步骤如下。
n = 3
arr = ["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
enum0 = arr.each_slice(n)
#=> #<Enumerator: ["1", "b", "0", "7", "9", "2", "a", "a",
# "5", "7"]:each_slice(3)>
enum1 = enum0.map
#=> #<Enumerator: #<Enumerator: ["1", "b", "0", "7", "9", "2", "a",
# "a", "5", "7"]:each_slice(3)>
这使用没有块的枚举#映射的形式。查看 enum1
的返回值。它可以被认为是一个复合枚举器,尽管Ruby没有这样的概念。注意:
enum1.each { |a| n.times.map { |i| a.fetch(i, false) } }
#=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", false, false]]
请参阅枚举器#每个。
生成enum1
的第一个元素并将其传递给块,块变量a
被分配其值:
a = enum1.next
#=> ["1", "b", "0"]
请参阅枚举器#下一个。现在执行块计算:
enum2 = n.times.map
#=> #<Enumerator: #<Enumerator: 3:times>:map>
i = enum2.next
#=> 0
a.fetch(i, false)
#=> ["1", "b", "0"].fetch(0, false)
#=> "1"
i = enum2.next
#=> 1
a.fetch(i, false)
#=> ["1", "b", "0"].fetch(1, false)
#=> "b"
i = enum2.next
#=> 2
a.fetch(i, false)
#=> ["1", "b", "0"].fetch(2, false)
#=> "0"
i = enum2.next
#=> StopIteration (iteration reached an end)
因此,map
返回["1", "b", "0"]
.计算类似于:
enum1.next
#=> ["7", "9", "2"]
和
enum1.next
#=> ["a", "a", "5"]
在所有这些情况下,我们本可以使用a[i]
代替a.fetch(i, false)
。
最后
a = enum1.next
#=> ["7"]
enum2 = n.times.map
#=> #<Enumerator: #<Enumerator: 3:times>:map>
enum2.each { |i| a.fetch(i, false) }
#=> ["7", false, false]
和以前一样,enum2
0
、1
和2
传递到区块。在后两种情况下,Array#fetch 返回其默认值 false
,因为索引是越界的。
其他选项
ary = ["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
n = 3
ary.then { |ary| ary + Array.new(- ary.size % n){false} }.each_slice(n)#.to_a
它在切片之前填充缺失的元素。
其他选项:
> array.each_slice(3).to_a.map{|e| Array.new(3){e.empty? ? false: e.shift }}
#=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", false, false]]
用nil
填充缺失的元素
> array.each_slice(3).to_a.map{|e| Array.new(3){e.shift }}
#=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", nil, nil]]