Here's another attempt for a variable numbers of columns. Given this array:
animals = [
['Cats', 'Dogs', 'Fish'],
['Mr. Tinkles', 'Buddy', 'Nemo'],
['Calico', 'Butch', 'Marlin'],
['Ginger', 'Ivy', 'Dory']
]
We can calculate the width of each column via transpose, map, length and max:
widths = animals.transpose.map { |x| x.map(&:length).max }
#=> [11, 5, 6]
Based on this, we can generate a format string that can be passed to sprintf (or its shortcut %):
row_format = widths.map { |w| "%-#{w}s" }.join(' ')
#=> "%-11s %-5s %-6s"
%s denotes a string argument, 11, 5 and 6 are our widths and - left-justifies the result.
Let's try it:
row_format % animals[0] #=> "Cats Dogs Fish "
row_format % animals[1] #=> "Mr. Tinkles Buddy Nemo "
row_format % animals[2] #=> "Calico Butch Marlin"
That looks good, we should use a loop and wrap everything it in a method:
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)
Output:
Cats Dogs Fish
Mr. Tinkles Buddy Nemo
Calico Butch Marlin
Ginger Ivy Dory
More complex formatting
With a little tweaking, you can also output a MySQL style 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)
Output:
+-------------+-------+--------+
| Cats | Dogs | Fish |
+-------------+-------+--------+
| Mr. Tinkles | Buddy | Nemo |
| Calico | Butch | Marlin |
| Ginger | Ivy | Dory |
+-------------+-------+--------+