Разбейте каждый_слайс на вложенные массивы и установите значения элементов по умолчанию, если последний вложенный массив имеет размер, отличный от других вложенных массивов. - PullRequest
1 голос
/ 02 июня 2019

У меня есть массив:

["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, которые поддерживают желаемое поведение?

Ответы [ 5 ]

3 голосов
/ 03 июня 2019

Вы можете посчитать количество необходимых элементов заполнения с помощью:

-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 в комментариях.

2 голосов
/ 02 июня 2019
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)>

Используется форма Enumerable # map , в которой нет блока. Посмотрите на возвращаемое значение 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, поскольку индекс выходит за пределы.

2 голосов
/ 02 июня 2019

#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) }
0 голосов
/ 04 июня 2019

Другая опция:

> 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]]
0 голосов
/ 02 июня 2019

Другой вариант

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

Заполняет недостающие элементы перед нарезкой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...