如何打印具有固定列宽的2D数组

时间:2016-05-09 18:08:04

标签: arrays ruby printing 2d

我有一个数组:

animals = [
  ["cats", "dogs"],
  ["verrylongcat", "dog"],
  ["shortcat", "verrylongdog"],
  ["cat", "dog"]
]

我想很好地展示它。是否有一种简单的方法可以使柱子固定宽度,所以我得到这样的东西:

cats            dogs
verrylongcat    dog
shortcat        verrylongdog
cat             dog

animals只是一个例子,我的数组也可能有3列,4列甚至更多。

3 个答案:

答案 0 :(得分:5)

您正在寻找String#ljust

max_cat_size = animals.map(&:first).max_by(&:size).size
animals.each do |cat, dog|
  puts "#{cat.ljust(max_cat_size)} #{dog}"
end

如果您想要多个空格,只需在插值中添加相应的数量。

假设你的阵列是 n×m 而不是 2×m

animal_max_sizes = animals.first.size.times.map do |index|
  animals.transpose[index].map(&:to_s).max_by(&:size).size
end

animals.map do |animal_line|
  animal_line.each.with_index.reduce('') do |animal_line, (animal, index)|
    animal_line + animal.to_s.ljust(animal_max_sizes[index].next)
  end
end.each { |animal_line_stringified| puts animal_line_stringified }

注意:如果您的数组包含to_s个,数字等,则会使用nil

答案 1 :(得分:4)

另一种方法是使用printf样式格式化。如果您知道每行中总是只有2个单词,那么您可以这样做:

#!/usr/bin/env ruby

lines = [
    '   cats dogs',
  '       verrylongcat dog',
  'shortcat verrylongdog  ',
    ' cat dog      ',
]

lines.map(&:strip).each do |line|
    puts "%-14s%s" % line.split
end

输出:

cats          dogs
verrylongcat  dog
shortcat      verrylongdog
cat           dog

如果您需要根据数据计算列宽,那么您还需要做更多的工作:

# as @ndn showed:
first_col_width = lines.map(&:split).map(&:first).max_by(&:size).size + 2

lines.map(&:strip).each do |line|
    puts "%-#{first_col_width}s%s" % line.split
end

答案 2 :(得分:1)

这是另一种可变数量列的尝试。给定这个数组:

animals = [
  ['Cats', 'Dogs', 'Fish'],
  ['Mr. Tinkles', 'Buddy', 'Nemo'],
  ['Calico', 'Butch', 'Marlin'],
  ['Ginger', 'Ivy', 'Dory']
]

我们可以通过transposemaplengthmax来计算每列的宽度:

widths = animals.transpose.map { |x| x.map(&:length).max }
#=> [11, 5, 6]

基于此,我们可以生成一个格式字符串,可以传递给sprintf(或其快捷方式%):

row_format = widths.map { |w| "%-#{w}s" }.join('   ')
#=> "%-11s   %-5s   %-6s"

%s表示字符串参数,1156是我们的宽度,-左对齐结果。

我们试一试:

row_format % animals[0]  #=> "Cats          Dogs    Fish  "
row_format % animals[1]  #=> "Mr. Tinkles   Buddy   Nemo  "
row_format % animals[2]  #=> "Calico        Butch   Marlin"

看起来不错,我们应该使用一个循环并将其包装在一个方法中:

def print_table(array)
  widths     = array.transpose.map { |x| x.map(&:length).max }
  row_format = widths.map { |w| "%-#{w}s" }.join('   ')

  array.each do |row_values|
    puts row_format % row_values
  end
end

print_table(animals)

输出:

Cats          Dogs    Fish  
Mr. Tinkles   Buddy   Nemo  
Calico        Butch   Marlin
Ginger        Ivy     Dory  

更复杂的格式

稍微调整一下,您还可以输出MySQL样式表:

def print_mysql_table(array)
  widths     = array.transpose.map { |x| x.map(&:length).max }
  row_format = '|%s|' % widths.map { |w| " %-#{w}s " }.join('|')
  separator  = '+%s+' % widths.map { |w| '-' * (w+2) }.join('+')

  header, *rows = array

  puts separator
  puts row_format % header
  puts separator
  rows.each do |row_values|
    puts row_format % row_values
  end
  puts separator
end

print_mysql_table(animals)

输出:

+-------------+-------+--------+
| Cats        | Dogs  | Fish   |
+-------------+-------+--------+
| Mr. Tinkles | Buddy | Nemo   |
| Calico      | Butch | Marlin |
| Ginger      | Ivy   | Dory   |
+-------------+-------+--------+