Reputation: 20086
Is there a way to output an array of arrays as a table in ruby? I'm looking for something that is useful from the console, like powershell's format-table
command.
An array that would be passed could be something like:
a = [[1.23, 5, :bagels], [3.14, 7, :gravel], [8.33, 11, :saturn]]
And the output could be something like:
-----------------------
| 1.23 | 5 | bagels |
| 3.14 | 7 | gravel |
| 8.33 | 11 | saturn |
-----------------------
Upvotes: 5
Views: 6175
Reputation: 107
I've made a small improvement on the @dart-egregious snippet
class Array
def to_table(header: true)
column_sizes = self.reduce([]) do |lengths, row|
row.each_with_index.map{|iterand, index| [lengths[index] || 0, iterand.to_s.length].max}
end
head = '+' + column_sizes.map{|column_size| '-' * (column_size + 2) }.join('+') + '+'
puts head
to_print = self.clone
if (header == true)
header = to_print.shift
print_table_data_row(column_sizes, header)
puts head
end
to_print.each{ |row| print_table_data_row(column_sizes, row) }
puts head
end
private
def print_table_data_row(column_sizes, row)
row = row.fill(nil, row.size..(column_sizes.size - 1))
row = row.each_with_index.map{|v, i| v = v.to_s + ' ' * (column_sizes[i] - v.to_s.length)}
puts '| ' + row.join(' | ') + ' |'
end
end
Using:
data = [
['column 1', 'column 2', 'ciolumn 3'],
['row 1 col 2', 'row 1 col 2', 'row 1 col 3'],
['row 1 col 2', 'row 1 col 2', 'row 1 col 3']
]
data.to_table
+-------------+-------------+-------------+
| column 1 | column 2 | ciolumn 3 |
+-------------+-------------+-------------+
| row 1 col 2 | row 1 col 2 | row 1 col 3 |
| row 1 col 2 | row 1 col 2 | row 1 col 3 |
+-------------+-------------+-------------+
data.to_table(header: false)
+-------------+-------------+-------------+
| column 1 | column 2 | ciolumn 3 |
| row 1 col 2 | row 1 col 2 | row 1 col 3 |
| row 1 col 2 | row 1 col 2 | row 1 col 3 |
+-------------+-------------+-------------+
Upvotes: 0
Reputation: 20086
I accepted sagarpandya82's answer, but here's my better implementation:
class Array
def to_table
column_sizes = self.reduce([]) do |lengths, row|
row.each_with_index.map{|iterand, index| [lengths[index] || 0, iterand.to_s.length].max}
end
puts head = '-' * (column_sizes.inject(&:+) + (3 * column_sizes.count) + 1)
self.each do |row|
row = row.fill(nil, row.size..(column_sizes.size - 1))
row = row.each_with_index.map{|v, i| v = v.to_s + ' ' * (column_sizes[i] - v.to_s.length)}
puts '| ' + row.join(' | ') + ' |'
end
puts head
end
end
Upvotes: 8
Reputation: 9497
You could try something like this:
a = [[1.23, 5, :bagels], [3.14, 7, :gravel], [8.33, 11, :saturn]]
bar = '-'*(a[0][0].to_s.length + 4 + a[0][1].to_s.length + 3 + a[0][2].to_s.length + 4)
puts bar
a.each do |i|
i.each.with_index do |j,k|
if k == 1 && j < 10
print "| #{j} "
else
print "| #{j} "
end
end
print '|'
puts
end
puts bar
returns:
----------------------
| 1.23 | 5 | bagels |
| 3.14 | 7 | gravel |
| 8.33 | 11 | saturn |
----------------------
bar
is just an estimate of how long the top and bottom dash-bar will be. In general this code checks each sub-array and prints out its elements with |
in the appropriate places. The only tricky bit is the second element of each sub-array. Here we check to see if it is double digit or single digit. The print-format is different for each.
Bear in mind that this code works specifically for your example and makes a lot of assumptions. Having said that it can be easily modified to taste.
Upvotes: 2