I would like to render a 2-dimensional list to a nice tabular output, using an ANSI escape sequences to control the formatting.
So given this data:
data = [
[ 100, 20, 30 ],
[ 20, 10, 20 ],
[ 50, 400, 20 ]
]
I would like to output something like this:
100 20 30
20 10 20
50 400 20
Many Thanks
In the "Programming Elixir" book by Dave Thomas there is a similar to what you need exercise todo:
write the code to format the data into columns, like the sample output at the start of the chapter:
# | Created at | Title
-----+----------------------+--------------------------------------------------
2722 | 2014-08-27T04:33:39Z | Added first draft of File.mv for moving files aro
2728 | 2014-08-29T14:28:30Z | Should elixir (and other applications) have stick
-----+----------------------+--------------------------------------------------
and there is a place where readers can post their solutions to this exercise where you can find and pick what suites you. You will need to modify your code though to make it work with your input data structure (an array of arrays, so a matrix) but that should be easy. If you have any trouble doing, this just ask.
BTW here is my solution I wrote while reading the book:
defmodule Issues.TableFormatter do
import Enum, only: [map: 2, max: 1, zip: 2, join: 2]
@header ~w(number created_at title)
@header_column_separator "-+-"
# TODO: Refactor; try to find more uses for stdlib functions
def print_table(rows, header \\ @header) do
table = rows |> to_table(header)
header = header |> map(&String.Chars.to_string/1) # the headers needs now to be a string
columns_widths = [header | table] |> columns_widths
hr = for _ <- 1..(length(header)), do: "-"
hr |> print_row(columns_widths, @header_column_separator)
header |> print_row(columns_widths)
hr |> print_row(columns_widths, @header_column_separator)
table |> map &(print_row(&1, columns_widths))
hr |> print_row(columns_widths, @header_column_separator)
end
def to_table(list_of_dicts, header) do
list_of_dicts
|> select_keys(header)
|> map(fn (dict) ->
dict
|> Dict.values
|> map(&String.Chars.to_string/1)
end)
end
def columns_widths(table) do
table
|> Matrix.transpose
|> map(fn (cell) ->
cell
|> map(&String.length/1)
|> max
end)
end
def select_keys(dict, keys) do
for entry <- dict do
{dict1, _} = Dict.split(entry, keys)
dict1
end
end
def print_row(row, column_widths, separator \\ " | ") do
padding = separator |> String.to_char_list |> List.first # Hack
IO.puts row
|> zip(column_widths)
|> map(fn ({ cell, column_width }) ->
String.ljust(cell, column_width, padding)
end)
|> join(separator)
end
end
Treat all this as inspiration not a direct solution to your problem. This may also be much more that what your needs are, but being able quickly format some tabular data in a generic way and print them in your terminal can be very handy in the future for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With