Я предположил, что a
просто содержит времена в b
, отсортированные.Поскольку это может быть вычислено, нет необходимости предоставлять эту информацию в качестве ввода.
Код
def print_table(data, time_label, column_spacing)
h = data.slice_before { |s| !s.include?('|') }.
each_with_object({}) { |(t,*a),h|
h[t] = a.map { |s| s.split('|') }.to_h.tap { |g| g.default = '' } }
row_labels = h.keys.sort
column_labels = h.values_at(*row_labels).reduce([]) { |a,g| a | g.keys }
image = [[time_label, *column_labels],
*row_labels.map { |time| [time, *h[time].values_at(*column_labels)] }]
row_label_width, *column_widths = image.transpose.map { |r| r.map(&:size).max }
print_image(image, row_label_width, column_widths, column_spacing)
end
def print_image(image, row_label_width, column_widths, column_spacing)
image.each do |time, *values|
print time.ljust(row_label_width)
values.zip(column_widths).each { |s,width| print s.rjust(width + column_spacing) }
puts
end
end
Пример
b = ["1240", "B|11", "F|8", "X|7",
"1015", "X|2", "D|5",
"1732", "D|9", "X|1", "B|3"]
time_label = "HOUR"
column_spacing = 2
print_table(b, time_label, column_spacing)
печать
HOUR X D B F
1015 2 5
1240 7 11 8
1732 1 9 3
Обратите внимание, что время в b
не в отсортированном порядке.
Пояснение
Для значений в разделе Пример первым шагом является группирование элементов массива b
в группировки (массивы) по времени суток.
groups = b.slice_before { |s| !s.include?('|') }
#=> #<Enumerator: #<Enumerator::Generator:0x000000022b2490>:each>
См. Enumerable # slice_before .Мы можем видеть объекты, которые будут сгенерированы этим перечислителем, преобразовав его в массив.
groups.to_a
#=> [["1240", "B|11", "F|8", "X|7"],
# ["1015", "X|2", "D|5"],
# ["1732", "D|9", "X|1", "B|3"]]
Далее, давайте преобразуем groups
в хеш.
h = groups.each_with_object({}) { |(t,*a),h|
h[t] = a.map { |s| s.split('|') }.
to_h.
tap { |g| g.default = '' } }
#=> {"1240"=>{"B"=>"11", "F"=>"8", "X"=>"7"},
# "1015"=>{"X"=>"2", "D"=>"5"},
# "1732"=>{"D"=>"9", "X"=>"1", "B"=>"3"}}
См. Enumerable # each_with_object , Array # to_h , Object # tap и Hash # default = .g.default = ''
присваивает хешу значение по умолчанию для пустого пространства.Это означает, что g[k]
возвращает пустое пространство, если g
не имеет ключа k
.Например, h["1015"]["B"] #=> ""
.g.default = ''
возвращает ''
, поэтому он заключен в блок tap
, который возвращает g
с определением по умолчанию.
В этой статье дается объяснениеиспользование оператора splat .(Здесь в двух словах: [1, *[2, 3]] #=> [1, 2, 3]
).
Для меток столбцов у нас есть несколько вариантов.Несмотря на это, нам сначала нужны уникальные ключи в значениях (хешах) h
, соответствующие ключам в row_labels
.
row_labels = h.keys.sort
#=> ["1015", "1240", "1732"]
column_labels = h.values_at(*row_labels)
#=> [{"X"=>"2", "D"=>"5"},
# {"B"=>"11", "F"=>"8", "X"=>"7"},
# {"D"=>"9", "X"=>"1", "B"=>"3"}]
column_labels = column_labels.reduce([]) { |a,g| a | g.keys }
#=> ["X", "D", "B", "F"]
См. Enumerable # values_at , Перечислимый # уменьшите (он же inject
) и Array # | .Я предположил, что это дает желаемый порядок столбцов, но элементы column_labels
могут быть переупорядочены при желании.В последнем разделе моего ответа я представлю два возможных варианта.
Далее мы создадим массив, содержащий все значения в таблице для печати.
image = [[time_label, *column_labels],
*row_labels.map { |time| [time, *h[time].values_at(*column_labels)] }]
#=> [["HOUR", "X", "D", "B", "F"],
# ["1015", "2", "5", "", ""],
# ["1240", "7", "", "11", "8"],
# ["1732", "1", "9", "3", ""]]
Enumerable#values_at
извлекает значения (хэши) в h[time]
, которые соответствуют каждой строке таблицы, в нужном порядке.
Затем мы можем напечатать таблицу как
row_label_width, *column_widths = image.transpose.map { |r| r.map(&:size).max }
# => [4, 1, 1, 2, 1]
, поэтому
row_label_width
#=> 4
column_widths
#=> [1, 1, 2, 1]
image.each do |time, *values|
print time.ljust(row_label_width)
values.zip(column_widths).each { |s,width| print s.rjust(width + column_spacing) }
puts
end
печатает таблицу, показанную в разделе Пример .
Порядок столбцов
Как я уже говорил ранее, элементы column_labels
могут быть переупорядочены при желании.Одной из возможностей является сортировка меток в алфавитном порядке.
column_labels = h.values_at(*row_labels).reduce([]) { |a,g| a | g.keys }.sort!
#=> ["B", "D", "F", "X"]
Другая заключается в том, что нам задан желаемый порядок всех возможных меток столбцов.
desired = ["Y", "F", "D", "Z", "B", "X"]
Затем вычислите следующее.
column_labels = desired & h.values_at(*row_labels).reduce([]) { |a,g| a | g.keys }
#=> ["F", "D", "B", "X"]