Packages
io_ansi_table
0.1.7
1.0.40
1.0.39
1.0.38
1.0.37
1.0.36
1.0.34
1.0.33
1.0.32
1.0.31
1.0.30
1.0.29
1.0.28
1.0.27
1.0.26
1.0.25
1.0.24
1.0.23
1.0.22
1.0.21
1.0.20
1.0.19
1.0.18
1.0.17
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.4.32
0.4.31
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Writes data to "stdio" in a table with borders and colors. Can choose a table style to change the look of the table.
Current section
Files
Jump to
Current section
Files
lib/io/ansi/table/formatter.ex
# Based on the book "Programming Elixir" by Dave Thomas.
defmodule IO.ANSI.Table.Formatter do
@moduledoc """
Prints a table applying a table style. Each row is
a list of values identified by successive headers.
"""
alias IO.ANSI.Table.Config
alias IO.ANSI.Table.Formatter.Helper
alias IO.ANSI.Table.Style
@doc """
Takes a list of key-value `collections`, the (maximum) number of
`collections` to format, whether to ring the `bell`, a table `style` and
up to four `options`:
- `headers` (keys)
- `key headers` to sort the `collections` on
- `header fixes` to alter the `headers`
- `margins` to position the table
Prints a table to STDOUT of the values in each selected `collection`.
The columns are identified by successive `headers` in order.
We calculate the width of each column to fit the longest element
in that column, also considering the `header` itself.
If the number of `collections` given is positive, we format
the first _n_ `collections` in the list once sorted. If negative,
the last _n_ ones.
## Parameters
- `collections` - list of collections (maps or keywords)
- `count` - number of collections to format (integer)
- `bell` - ring the bell? (boolean)
- `style` - table style (atom)
- `options` - headers, key headers, header fixes and margins (keyword)
## Options
- `:headers` - defaults to config value `:headers` (list)
- `:key_headers` - defaults to config value `:key_headers` (list)
- `:header_fixes` - defaults to config value `:header_fixes` (map)
- `:margins` - defaults to config value `:margins` (keyword)
## Table styles
#{Style.texts " - `&style`&filler - ¬e\n"}
## Examples
alias IO.ANSI.Table.Formatter
people = [
%{name: "Mike", likes: "ski, arts", date_of_birth: "1992-04-15"},
%{name: "Mary", likes: "reading" , date_of_birth: "1985-07-11"},
%{name: "Ray" , likes: "cycling" , date_of_birth: "1977-08-28"}
]
Formatter.print_table(
people, 3, true, :dark,
headers: [:name, :date_of_birth, :likes],
key_headers: [:date_of_birth],
header_fixes: %{~r[\sof\s]i => "\sof\s"},
margins: [top: 2, bottom: 2, left: 2]
)
## 
iex> alias IO.ANSI.Table.Formatter
iex> alias ExUnit.CaptureIO
iex> people = [
...> %{name: "Mike", likes: "ski, arts", date_of_birth: "1992-04-15"},
...> %{name: "Mary", likes: "reading" , date_of_birth: "1985-07-11"},
...> %{name: "Ray" , likes: "cycling" , date_of_birth: "1977-08-28"}
...> ]
iex> CaptureIO.capture_io fn ->
...> Formatter.print_table(
...> people, 3, false, :dashed,
...> headers: [:name, :date_of_birth, :likes],
...> key_headers: [:date_of_birth],
...> header_fixes: %{~r[\sof\s]i => "\sof\s"},
...> margins: [top: 0, bottom: 0, left: 0]
...> )
...> end
\"""
+------+---------------+-----------+
| Name | Date of Birth | Likes |
+------+---------------+-----------+
| Ray | 1977-08-28 | cycling |
| Mary | 1985-07-11 | reading |
| Mike | 1992-04-15 | ski, arts |
+------+---------------+-----------+
\"""
"""
@spec print_table([map | Keyword.t], integer, boolean, atom, Keyword.t)
:: :ok
def print_table(collections, count, bell, style, options \\ []) do
headers = Keyword.get(options, :headers, Config.headers)
key_headers = Keyword.get(options, :key_headers, Config.key_headers)
header_fixes = Keyword.get(options, :header_fixes, Config.header_fixes)
margins = Config.margins Keyword.get(options, :margins)
collections =
collections
|> Stream.map(&Map.take &1, headers) # optional
|> Enum.sort_by(&key_for &1, key_headers)
|> Enum.take(count)
widths =
[Map.new(headers, &{&1, titlecase(&1, header_fixes)}) | collections]
|> columns(headers)
|> widths # => max widths of column values or headers
rows = rows(collections, headers)
Helper.print_table(
rows, headers, key_headers, header_fixes, margins, widths, style, bell
)
end
@doc """
Takes a key-value `collection` and a list of `keys`.
Returns a sort key for the `collection`.
## Examples
iex> alias IO.ANSI.Table.Formatter
iex> collection = [one: '1', two: "2", three: 3.0, four: 4]
iex> keys = [:three, :one, :four]
iex> Formatter.key_for(collection, keys)
"3.014"
"""
@spec key_for([map | Keyword.t], [any]) :: String.t
def key_for(collection, keys) do
Enum.map_join keys, &collection[&1]
end
@doc """
Takes a list of key-value `collections` and a list of `keys`.
Returns a list of columns, each column being a list of string values
selected from each `collection` on a given `key` and repeatedly on
successive `keys` in order.
## Examples
iex> alias IO.ANSI.Table.Formatter
iex> list = [
...> %{"a" => "1", "b" => "2", "c" => "3"},
...> %{"a" => "4", "b" => "5", "c" => "6"}
...> ]
iex> Formatter.columns(list, ["c", "a"])
[["3", "6"], ["1", "4"]]
iex> alias IO.ANSI.Table.Formatter
iex> list = [
...> %{:one => "1", '2' => 2.0, 3 => :three},
...> %{:one => '4', '2' => "5", 3 => 000006}
...> ]
iex> Formatter.columns(list, [3, :one, '2'])
[["three", "6"], ["1", "4"], ["2.0", "5"]]
"""
@spec columns([map | Keyword.t], [any]) :: [[String.t]]
def columns(collections, keys) do
for key <- keys do
for collection <- collections, do: to_string collection[key]
end
end
@doc """
Takes a list of key-value `collections` and a list of `keys`.
Returns a list of rows, each row being a list of string values
orderly selected on successive `keys` from a given `collection` and
repeatedly for each `collection` in turn.
## Examples
iex> alias IO.ANSI.Table.Formatter
iex> list = [
...> %{"a" => "1", "b" => "2", "c" => "3"},
...> %{"a" => "4", "b" => "5", "c" => "6"}
...> ]
iex> Formatter.rows(list, ["c", "a"])
[["3", "1"], ["6", "4"]]
iex> alias IO.ANSI.Table.Formatter
iex> list = [
...> %{:one => "1", '2' => 2.0, 3 => :three},
...> %{:one => '4', '2' => "5", 3 => 000006}
...> ]
iex> Formatter.rows(list, [3, :one, '2'])
[["three", "1", "2.0"], ["6", "4", "5"]]
"""
@spec rows([map | Keyword.t], [any]) :: [[String.t]]
def rows(collections, keys) do
for collection <- collections do
for key <- keys, do: to_string collection[key]
end
end
@doc """
Given a list of `columns` (string sublists), returns a list containing
the maximum width of each `column`.
## Examples
iex> alias IO.ANSI.Table.Formatter
iex> data = [["cat", "wombat", "elk"], ["mongoose", "ant", "gnu"]]
iex> Formatter.widths(data)
[6, 8]
iex> alias IO.ANSI.Table.Formatter
iex> data = [[":cat", "{1, 2}", "007"], ["mongoose", ":ant", "3.1416"]]
iex> Formatter.widths(data)
[6, 8]
"""
@spec widths([[String.t]]) :: [non_neg_integer]
def widths(columns) do
for column <- columns, do: column |> Enum.map(&String.length/1) |> Enum.max
end
@doc ~S"""
Uppercases the first letter of every "word" of a `title` (must be
convertible to a string).
Any underscore is considered a space and consecutive
whitespace characters are treated as a single occurrence.
Leading and trailing whitespace characters are removed.
If a `fixes` map is given, all occurrences of each `fix` key in the
`title` will be replaced with the corresponding `fix` value.
## Examples
iex> alias IO.ANSI.Table.Formatter
iex> Formatter.titlecase(" son of a gun ", %{
...> ~r[\sof\s]i => "\sof\s",
...> ~r[\sa\s]i => "\sa\s"
...> })
"Son of a Gun"
iex> alias IO.ANSI.Table.Formatter
iex> Formatter.titlecase("_date___of_birth_", %{
...> ~r[\sof\s]i => "\sof\s"
...> })
"Date of Birth"
iex> alias IO.ANSI.Table.Formatter
iex> Formatter.titlecase(:" _an_ _oDD case_ ")
"An ODD Case"
"""
@spec titlecase(any, map) :: String.t
def titlecase(title, fixes \\ %{}) do
import Enum, only: [map_join: 3, reduce: 3]
import String, only: [first: 1, replace: 3, slice: 2, split: 3, upcase: 1]
title =
title
|> to_string
|> split(~r/(_|\s)+/, trim: true)
|> map_join("\s", &(upcase(first &1) <> slice &1, 1..-1))
reduce fixes, title, &replace(&2, elem(&1, 0), elem(&1, 1))
end
end