Print clojure maps as a human-readable table.
Add the latest version of library to your app.
For leiningen or boot:
[com.joshuadavey/crockery "<version>"]}
or, for deps.edn:
{com.joshuadavey/crockery {:mvn/version "<latest version>"}}
Require crockery.core, which contains the primary API:
(require '[crockery.core :as crockery])
The examples that follow use this data:
(def people
[{:first-name "Alice", :last-name "Anderson", :age 32, :dues "98.00"}
{:first-name "Bob", :last-name "Bobberson", :age 29, :dues "17"},
{:first-name "Carol", :last-name "Carola", :age 26, :dues "105.50"},
{:first-name "Doug", :last-name "Duggler", :age 41, :dues "0.125"}])
Print a table, inferring column names from the first map of the collection:
(crockery/print-table people)
|------------+-----------+-----+--------|
| First Name | Last Name | Age | Dues |
|------------+-----------+-----+--------|
| Alice | Anderson | 32 | 98.00 |
| Bob | Bobberson | 29 | 17 |
| Carol | Carola | 26 | 105.50 |
| Doug | Duggler | 41 | 0.125 |
|------------+-----------+-----+--------|
Or, specify the columns you want included:
(crockery/print-table [:age :last-name] people)
|-----+-----------|
| Age | Last Name |
|-----+-----------|
| 32 | Anderson |
| 29 | Bobberson |
| 26 | Carola |
| 41 | Duggler |
|-----+-----------|
You can mix and match colspec forms (maps and keywords):
(crockery/print-table [{:name :last-name, :align :right} :first-name] people)
|-----------+------------|
| Last Name | First Name |
|-----------+------------|
| Anderson | Alice |
| Bobberson | Bob |
| Carola | Carol |
| Duggler | Doug |
|-----------+------------|
In addition to a collection of maps, table accepts a few other data shapes.
A plain map is rendered as a two-column key/value table:
(crockery/print-table (array-map :first-name "Alice" :last-name "Anderson" :age 32))
|-------------+----------|
| Key | Value |
|-------------+----------|
| :first-name | Alice |
| :last-name | Anderson |
| :age | 32 |
|-------------+----------|
A sequence of sequences is rendered using the first inner sequence as column headers:
(crockery/print-table [["Name" "Score"]
["Alice" 95]
["Bob" 87]])
|-------+-------|
| Name | Score |
|-------+-------|
| Alice | 95 |
| Bob | 87 |
|-------+-------|
In map form, most keys are optional, but a colspec must have at least :name or :key-fn and :title.
Use a keyword for both the getter function and the title of the column (titleized):
(crockery/print-table [{:name :age} {:name :last-name}] people)
|-----+-----------|
| Age | Last Name |
|-----+-----------|
| 32 | Anderson |
| 29 | Bobberson |
| 26 | Carola |
| 41 | Duggler |
|-----+-----------|
Specify a different accessor function. It should be a function that takes one arg, and will be called for each "row" in the collection.
(crockery/print-table
[{:title "Age (months)" :key-fn (comp (partial * 12) :age)}
:first-name]
people)
|--------------+------------|
| Age (months) | First Name |
|--------------+------------|
| 384 | Alice |
| 348 | Bob |
| 312 | Carol |
| 492 | Doug |
|--------------+------------|
Widths are normally calculated by finding the longest string per column, but you can also specify one:
(crockery/print-table [{:name :age, :width 10} {:name :last-name, :width 5}] people)
|------------+-------|
| Age | Last |
|------------+-------|
| 32 | Ander |
| 29 | Bobbe |
| 26 | Carol |
| 41 | Duggl |
|------------+-------|
Values that are too long will be truncated.
One of #{:left :center :right}, defaults to :left. Affects the data rows. When no :title-align is specified, also affects the header.
One special alignment, :decimal, attempts to line up the column so that the decimal point is in the same place in each row:
(crockery/print-table [:first-name :last-name {:name :dues :align :decimal}] people)
|------------+-----------+---------|
| First Name | Last Name | Dues |
|------------+-----------+---------|
| Alice | Anderson | 98.00 |
| Bob | Bobberson | 17 |
| Carol | Carola | 105.50 |
| Doug | Duggler | 0.125 |
|------------+-----------+---------|
Since numbers are printed as strings, in reality, the decimal alignment just looks for the first decimal point as an alignment anchor.
Provide your own header title rather than titleizing the :name parameter.
(crockery/print-table [:last-name {:name :first-name, :title "Given name"}] people)
|-----------+------------|
| Last Name | Given name |
|-----------+------------|
| Anderson | Alice |
| Bobberson | Bob |
| Carola | Carol |
| Duggler | Doug |
|-----------+------------|
Same properties as :align, but only affects the header.
A function applied to each cell value (after :key-fn, before escaping) to produce the display string. Defaults to str, with special handling for nil (empty string) and java.util.Date. Must return a string.
(crockery/print-table
[:first-name
{:name :age, :render-cell #(str % " yrs")}]
people)
|------------+--------|
| First Name | Age |
|------------+--------|
| Alice | 32 yrs |
| Bob | 29 yrs |
| Carol | 26 yrs |
| Doug | 41 yrs |
|------------+--------|
A function applied to the column's :name (or :key-fn) to produce the header string, instead of the default titleization. Must return a string.
(crockery/print-table
[{:name :first-name, :render-title name} :age]
people)
|------------+-----|
| first-name | Age |
|------------+-----|
| Alice | 32 |
| Bob | 29 |
| Carol | 26 |
| Doug | 41 |
|------------+-----|
When true and a cell value exceeds :width, the value is truncated and ... is appended rather than cutting off abruptly.
(crockery/print-table [{:name :last-name, :width 7, :ellipsis true}] people)
|---------|
| Last... |
|---------|
| Ande... |
| Bobb... |
| Carola |
| Duggler |
|---------|
Note: :ellipsis is also set automatically on columns that are shrunk by :max-width rebalancing.
When true, ANSI escape codes in cell values are excluded from width calculations and truncation. Useful when passing pre-colored strings to the table so that the visual width is correct.
Options are passed as a map, either as the first argument to table or print-table, or by rebinding crockery.core/*default-options*.
The output format. Can be a built-in format keyword (e.g. :org, :gfm, :fancy) or any value implementing crockery.protocols/RenderTable. Defaults to :org.
An alternative to passing columns as a separate argument — useful when passing everything in a single options map.
(crockery/print-table {:columns [:first-name :age]} people)
|------------+-----|
| First Name | Age |
|------------+-----|
| Alice | 32 |
| Bob | 29 |
| Carol | 26 |
| Doug | 41 |
|------------+-----|
A map of colspec defaults applied to every column before per-column options. Useful for setting a global alignment or other option.
(crockery/print-table {:defaults {:align :right}} [:first-name :age] people)
|------------+-----|
| First Name | Age |
|------------+-----|
| Alice | 32 |
| Bob | 29 |
| Carol | 26 |
| Doug | 41 |
|------------+-----|
The maximum total character width of the rendered table. When the auto-calculated width exceeds this, columns are proportionally shrunk and :ellipsis is enabled on the affected columns. Defaults to the detected terminal width (via the COLUMNS environment variable, stty, or tput).
When false, suppresses the header row entirely. Defaults to true.
The default output format is an :org, which outputs an org-mode compatible table. There are other built-in formats that can be used. You can specify the format with the :format key in the options map, either before other arguments, or globally by rebinding the crockery.core/*default-options* var.
For each of these formats, the following colspec will be used:
(def colspec [:last-name
{:name :first-name
:title "Given name"
:align :right}
:age])
This is the default format. See any of the above examples.
A fixed-width, unadorned output format.
(crockery/print-table {:format :plain} colspec people)
Last Name Given name Age
Anderson Alice 32
Bobberson Bob 29
Carola Carol 26
Duggler Doug 41
(crockery/print-table {:format :simple} colspec people)
Last Name Given name Age
--------- ---------- ---
Anderson Alice 32
Bobberson Bob 29
Carola Carol 26
Duggler Doug 41
(crockery/print-table {:format :grid} colspec people)
+-----------+------------+-----+
| Last Name | Given name | Age |
+===========+============+=====+
| Anderson | Alice | 32 |
+-----------+------------+-----+
| Bobberson | Bob | 29 |
+-----------+------------+-----+
| Carola | Carol | 26 |
+-----------+------------+-----+
| Duggler | Doug | 41 |
+-----------+------------+-----+
Another fixed-width format, with no surrounding border.
(crockery/print-table {:format :presto} colspec people)
Last Name | Given name | Age
-----------|------------|-----
Anderson | Alice | 32
Bobberson | Bob | 29
Carola | Carol | 26
Duggler | Doug | 41
Based on the reStructured Text table format.
(crockery/print-table {:format :rst} colspec people)
========= ========== ===
Last Name Given name Age
========= ========== ===
Anderson Alice 32
Bobberson Bob 29
Carola Carol 26
Duggler Doug 41
========= ========== ===
This format uses unicode pipe characters. :fancy-grid is also available if you'd like separators between data rows.
(crockery/print-table {:format :fancy} colspec people)
┌───────────┬────────────┬─────┐
│ Last Name │ Given name │ Age │
├───────────┼────────────┼─────┤
│ Anderson │ Alice │ 32 │
│ Bobberson │ Bob │ 29 │
│ Carola │ Carol │ 26 │
│ Duggler │ Doug │ 41 │
└───────────┴────────────┴─────┘
Like :fancy, but with bolder lines. :heavy-grid is also available if you'd like separators between data rows.
(crockery/print-table {:format :heavy} colspec people)
┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━┓
┃ Last Name ┃ Given name ┃ Age ┃
┣━━━━━━━━━━━╋━━━━━━━━━━━━╋━━━━━┫
┃ Anderson ┃ Alice ┃ 32 ┃
┃ Bobberson ┃ Bob ┃ 29 ┃
┃ Carola ┃ Carol ┃ 26 ┃
┃ Duggler ┃ Doug ┃ 41 ┃
┗━━━━━━━━━━━┻━━━━━━━━━━━━┻━━━━━┛
Like :fancy, but with rounded corners. :rounded-grid is also available if you'd like separators between data rows.
(crockery/print-table {:format :rounded} colspec people)
╭───────────┬────────────┬─────╮
│ Last Name │ Given name │ Age │
├───────────┼────────────┼─────┤
│ Anderson │ Alice │ 32 │
│ Bobberson │ Bob │ 29 │
│ Carola │ Carol │ 26 │
│ Duggler │ Doug │ 41 │
╰───────────┴────────────┴─────╯
Like :fancy, but with double-line borders. :double-grid is also available if you'd like separators between data rows.
(crockery/print-table {:format :double} colspec people)
╔═══════════╦════════════╦═════╗
║ Last Name ║ Given name ║ Age ║
╠═══════════╬════════════╬═════╣
║ Anderson ║ Alice ║ 32 ║
║ Bobberson ║ Bob ║ 29 ║
║ Carola ║ Carol ║ 26 ║
║ Duggler ║ Doug ║ 41 ║
╚═══════════╩════════════╩═════╝
Like :fancy-grid, but with bolder separators between the header and body rows.
(crockery/print-table {:format :mixed-grid} colspec people)
┍━━━━━━━━━━━┯━━━━━━━━━━━━┯━━━━━┑
│ Last Name │ Given name │ Age │
┝━━━━━━━━━━━┿━━━━━━━━━━━━┿━━━━━┥
│ Anderson │ Alice │ 32 │
├───────────┼────────────┼─────┤
│ Bobberson │ Bob │ 29 │
├───────────┼────────────┼─────┤
│ Carola │ Carol │ 26 │
├───────────┼────────────┼─────┤
│ Duggler │ Doug │ 41 │
┕━━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━┙
This tab-delimited format doesn't look great when printed directly, but is convenient for further processing with common unix utils. Alignment options are ignored.
(crockery/print-table {:format :tsv} colspec people)
Last Name Given name Age
Anderson Alice 32
Bobberson Bob 29
Carola Carol 26
Duggler Doug 41
Github-flavored Markdown (GFM) extends standard Markdown with a table syntax, including alignment designators.
(crockery/print-table {:format :gfm} colspec people)
| Last Name | Given name | Age |
|:----------|-----------:|:----|
| Anderson | Alice | 32 |
| Bobberson | Bob | 29 |
| Carola | Carol | 26 |
| Duggler | Doug | 41 |
Copyright © 2021 Joshua Davey
Distributed under the Eclipse Public License version 1.0.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |