Вывести элементы массива массивов разного размера в одной строке в Ruby
Может быть, кто-то может помочь мне с этим. У меня есть массив массивов. Внутренние массивы имеют разные размеры (от 2 до 4 элементов).
letters = [["A", "B"],["C", "D", "F", "G"],["H", "I", "J" ]]
Я пытаюсь напечатать в одной строке каждый массив, как первый элемент столбца [0] и элемент [1], соединенные, как элемент 2-го столбца [0], элемент [1], элемент [2], объединенный в качестве элемента 3-го столбца [ 0], элемент [1], элемент [3] объединены. Элементы 2 и 3 не всегда существуют.
Вывод, который я пытаюсь получить, выглядит следующим образом:
AB
CD CDF CDG
HI HIJ
Я делаю таким образом, но я получаю эту ошибку.
letters.map{|x| puts x[0]+x[1] + "," + x[0]+x[1]+x[2] + "," + x[0]+x[1]+x[3]}
TypeError: no implicit conversion of nil into String
from (irb):1915:in "+"
from (irb):1915:in "block in irb_binding"
from (irb):1915:in "map"
from (irb):1915
from /usr/bin/irb:11:in "<main>"
3 ответа
letters.each do |a,b,*rest|
puts rest.each_with_object([a+b]) { |s,arr| arr << arr.first + s }.join(' ')
end
печать
AB
CD CDF CDG
HI HIJ
Шаги следующие.
предполагать
letters = [["C", "D", "F", "G"],["H", "I", "J" ]]
затем
enum0 = letters.each
#=> #<Enumerator: [["C", "D", "F", "G"], ["H", "I", "J"]]:each>
Первый элемент этого перечислителя генерируется и передается в блок, а трем переменным блока присваиваются значения.
a, b, *rest = enum0.next
#=> ["C", "D", "F", "G"]
a
#=> "C"
b
#=> "D"
rest
#=> ["F", "G"]
Далее получаем
enum1 = rest.each_with_object([a+b])
#=> rest.each_with_object(["CD"])
#=> #<Enumerator: ["F", "G"]:each_with_object(["CD"])>
Первый элемент этого перечислителя генерируется и передается в блок, а переменным блока присваиваются значения.
s, arr = enum1.next
#=> ["F", ["CD"]]
s
#=> "F"
arr
#=> ["CD"]
Расчет блока теперь выполняется.
arr << arr.first + s
#=> arr << "CD" + "F"
#=> ["CD", "CDF"]
Второй и последний элемент enum1
генерируется и передается в блок, а переменным блока присваиваются значения, и блок вычисляется.
s, arr = enum1.next
#=> ["G", ["CD", "CDF"]]
arr << arr.first + s
#=> ["CD", "CDF", "CDG"]
Когда попытка создать другой элемент из enum1
мы получаем
enum1.next
#StopIteration: iteration reached an end
Ruby обрабатывает исключение, вырываясь из блока и возвращая arr
, Элементы arr
затем присоединяются:
arr.join(' ')
#=> "CD CDF CDG"
и напечатано.
Второй и последний элемент enum0
теперь генерируется, передается в блок, и трем переменным блока присваиваются значения.
a, b, *rest = enum0.next
#=> ["H", "I", "J"]
a
#=> "H"
b
#=> "I"
rest
#=> ["J"]
Остальные расчеты аналогичны.
Некоторые читатели могут быть незнакомы с методом Enumerable # each_with_object, который широко используется. Прочитайте документ, но обратите внимание, что здесь он дает тот же результат, что и код, написанный следующим образом.
letters.each do |a,b,*rest|
arr = [a+b]
rest.each { |s| arr << arr.first + s }
puts arr.join(' ')
end
Используя each_with_object
мы избегаем необходимости в утверждении arr = [a+b]
и утверждение puts arr.join(' ')
, Функции этих двух операторов, конечно, там в строке, используя each_with_object
, но большинство пользователей Ruby предпочитают поток, когда при цепочке each_with_object
в join(' ')
, Еще одно отличие заключается в том, что значение arr
ограничивается each_with_object
Блок, который является хорошей практикой программирования.
Похоже, вы хотите соединить первые две буквы, а затем взять декартово произведение с остальными.
letters.each do |arr|
first = arr.take(2).join
rest = arr.drop(2)
puts [first, [first].product(rest).map(&:join)].join(" ")
end
Это обеспечивает точный вывод, который вы указали.
Просто из любопытства, Enumerable#map
Единственное решение.
letters = [["A", "B"],["C", "D", "F", "G"],["H", "I", "J" ]]
letters.map do |f, s, *rest|
rest.unshift(nil).map { |l| [f, s, l].join }.join(' ')
end.each(&method(:puts))
#⇒ AB
# CD CDF CDG
# HI HIJ