Ruby each_slice到子数组中,如果最后一个子数组的大小与其他子数组不同,则设置默认元素值



我有一个数组:

["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 012传递到区块。在后两种情况下,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]]

最新更新