如何打印具有固定列宽的二维数组
How to print a 2D array with fixed column width
我有一个数组:
animals = [
["cats", "dogs"],
["verrylongcat", "dog"],
["shortcat", "verrylongdog"],
["cat", "dog"]
]
而且我想很好地展示它。有没有一种简单的方法可以使列的宽度固定,所以我得到这样的结果:
cats dogs
verrylongcat dog
shortcat verrylongdog
cat dog
animals
只是一个例子,我的数组也可以有 3 或 4 列甚至更多。
您正在寻找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
如果你想要多个space只需在插值中添加相应的数量即可。
假设你的数组是 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 }
注意: 如果您的数组包含 nil
、数字等,则使用 to_s
es。
另一种方法是使用 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
这是对可变列数的另一种尝试。给定这个数组:
animals = [
['Cats', 'Dogs', 'Fish'],
['Mr. Tinkles', 'Buddy', 'Nemo'],
['Calico', 'Butch', 'Marlin'],
['Ginger', 'Ivy', 'Dory']
]
我们可以通过transpose
, map
, length
and max
:
来计算每一列的宽度
widths = animals.transpose.map { |x| x.map(&:length).max }
#=> [11, 5, 6]
基于此,我们可以生成格式字符串,可以传递给sprintf
(or its shortcut %
):
row_format = widths.map { |w| "%-#{w}s" }.join(' ')
#=> "%-11s %-5s %-6s"
%s
表示字符串参数,11
、5
和 6
是我们的宽度,-
左对齐结果。
让我们试试看:
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 样式 table:
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 |
+-------------+-------+--------+
我有一个数组:
animals = [
["cats", "dogs"],
["verrylongcat", "dog"],
["shortcat", "verrylongdog"],
["cat", "dog"]
]
而且我想很好地展示它。有没有一种简单的方法可以使列的宽度固定,所以我得到这样的结果:
cats dogs
verrylongcat dog
shortcat verrylongdog
cat dog
animals
只是一个例子,我的数组也可以有 3 或 4 列甚至更多。
您正在寻找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
如果你想要多个space只需在插值中添加相应的数量即可。
假设你的数组是 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 }
注意: 如果您的数组包含 nil
、数字等,则使用 to_s
es。
另一种方法是使用 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
这是对可变列数的另一种尝试。给定这个数组:
animals = [
['Cats', 'Dogs', 'Fish'],
['Mr. Tinkles', 'Buddy', 'Nemo'],
['Calico', 'Butch', 'Marlin'],
['Ginger', 'Ivy', 'Dory']
]
我们可以通过transpose
, map
, length
and max
:
widths = animals.transpose.map { |x| x.map(&:length).max }
#=> [11, 5, 6]
基于此,我们可以生成格式字符串,可以传递给sprintf
(or its shortcut %
):
row_format = widths.map { |w| "%-#{w}s" }.join(' ')
#=> "%-11s %-5s %-6s"
%s
表示字符串参数,11
、5
和 6
是我们的宽度,-
左对齐结果。
让我们试试看:
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 样式 table:
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 |
+-------------+-------+--------+