ruby 35 lines · 8 steps

Rendering an ASCII table in Ruby

Turn an array of row hashes into an aligned, bordered text table by measuring column widths first.

Explained by highlit
1def render_table(rows)
2 return "(no data)" if rows.empty?
3 
4 columns = rows.flat_map(&:keys).uniq
5 widths = columns.each_with_object({}) do |col, acc|
6 cell_lengths = rows.map { |row| stringify(row[col]).length }
7 acc[col] = [col.to_s.length, *cell_lengths].max
8 end
9 
10 separator = "+" + columns.map { |col| "-" * (widths[col] + 2) }.join("+") + "+"
11 header = format_row(columns.map { |col| [col, col.to_s] }, widths)
12 
13 lines = [separator, header, separator]
14 rows.each do |row|
15 cells = columns.map { |col| [col, stringify(row[col])] }
16 lines << format_row(cells, widths)
17 end
18 lines << separator
19 
20 lines.join("\n")
21end
22 
23def format_row(cells, widths)
24 padded = cells.map { |col, value| " #{value.ljust(widths[col])} " }
25 "|" + padded.join("|") + "|"
26end
27 
28def stringify(value)
29 case value
30 when nil then ""
31 when Hash then value.map { |k, v| "#{k}=#{stringify(v)}" }.join(", ")
32 when Array then value.map { |v| stringify(v) }.join(", ")
33 else value.to_s
34 end
35end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Measure every column's maximum width before drawing so all cells align consistently.
  2. 2Collecting keys across all rows lets you handle rows with differing shapes.
  3. 3A recursive stringifier gives you predictable cell text for nested values like hashes and arrays.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Rendering an ASCII table in Ruby — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code