Metadata-Version: 2.4
Name: table_maker
Version: 0.2.0
Summary: Create simple, tastefully-formatted strings that resemble tables
Author-email: "I.R. Whitt" <yvlcmb@protonmail.com>
License-Expression: MIT
Project-URL: Homepage, https://gitlab.com/ratbad/table-maker
Project-URL: Bug Reports, https://gitlab.com/ratbad/table-maker/issues
Project-URL: Source, https://gitlab.com/ratbad/table-maker/
Keywords: table,text,ascii,formatting,monospace
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Topic :: Text Processing
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Provides-Extra: test
Requires-Dist: coverage; extra == "test"
Requires-Dist: pytest; extra == "test"

# Table Maker

## Description
Make simple tables from rows and columns, with values separated into cells and the contents left-justified.

Initially designed for creating map marginalia in QGIS and ArcMap, as well as a substitute for the often over-engineered and clunky table wizards that are featured in word processors.

The design is data-oriented: a table is just a pair of plain values — `cols` (column names) and `rows` (same-length sequences) — and every function is a pure transformation of those values. Rendering to a string happens once, at the end, via `render`. Transformations that take configuration (`sort_rows`, `head`, `tail`, `render`) are factories: called with their config, they return a function of the data, so pipelines built with `pipe` read left to right.

## Installation
`pip install table_maker`

## Usage
Most basic use is to create a simple, cleanly-formatted table:
```python
>>> from table_maker import pipe, render, sort_rows
>>> cols = ('athlete', 'time')
>>> rows = (('Clint', '16:04'), ('Mitch', '12:12'), ('Tommy', '22:57'), ('Zach', '27:56'))
>>> print(pipe(rows, sort_rows(col=1), render(cols)))
+---------+-------+
| athlete | time  |
+=========+=======+
| Mitch   | 12:12 |
+---------+-------+
| Clint   | 16:04 |
+---------+-------+
| Tommy   | 22:57 |
+---------+-------+
| Zach    | 27:56 |
+---------+-------+
```
`render(cols, rows)` also works directly when there's nothing to compose.

That's mostly it, but there are a few simple utilities. You can add row numbers if you like — `numbered` takes and returns the `(cols, rows)` pair, since it adds a column:
```python
>>> import table_maker as tm
>>> cols = ('u-boat', 'commissioned', 'sunk')
>>> rows = (
...  ('u-64', '16 Dec 39', '13 Apr 40'),
...  ('u-104', '19 Aug 40', '28 Nov 40'),
...  ('u-107', '08 Oct 40', '08 Aug 44'))
>>> print(tm.render(*tm.numbered(cols, rows)))
+---+--------+--------------+-----------+
| # | u-boat | commissioned | sunk      |
+===+========+==============+===========+
| 1 | u-64   | 16 Dec 39    | 13 Apr 40 |
+---+--------+--------------+-----------+
| 2 | u-104  | 19 Aug 40    | 28 Nov 40 |
+---+--------+--------------+-----------+
| 3 | u-107  | 08 Oct 40    | 08 Aug 44 |
+---+--------+--------------+-----------+
```
Or you can drop the separators between rows for a more compact table, and convert items to title case:
```python
>>> import table_maker as tm
>>> cols = ('album', 'year')
>>> rows = (('fandango!', '1975'), ('tres hombres', '1973'), ('eliminator', '1983'))
>>> x, y = tm.title_case(cols, tm.pipe(rows, tm.sort_rows(col=1)))
>>> print(tm.render(x, y, row_seps=False))
+--------------+------+
| Album        | Year |
+==============+======+
| Tres Hombres | 1973 |
| Fandango!    | 1975 |
| Eliminator   | 1983 |
+--------------+------+
```
There are no utilities for selecting rows beyond `head` and `tail` — since the data is plain tuples, filtering is an ordinary comprehension. For example, from this CSV string:
```python
>>> csv = '''first,last,GOATscore
Kareem,Abdul-Jabbar,5.600
LeBron,James,5.511
Michael,Jordan,5.219
Tim,Duncan,4.273
Bill,Russell,4.066
Kobe,Bryant,4.021
Wilt,Chamberlain,3.885'''
```
If you only want players whose score is above 4, get them before rendering:
```python
>>> import table_maker as tm
>>> lines = csv.split('\n')
>>> cols = lines[0].split(',')
>>> rows = [line.split(',') for line in lines[1:] if line]
>>> above_4 = [row for row in rows if float(row[2]) > 4]
>>> ranked = tm.pipe(above_4, tm.sort_rows(col=2, key=float, reverse=True))
>>> print(tm.render(*tm.numbered(cols, ranked), title='THE GREATEST OF ALL TIME'))
THE GREATEST OF ALL TIME
+---+---------+--------------+-----------+
| # | first   | last         | GOATscore |
+===+=========+==============+===========+
| 1 | Kareem  | Abdul-Jabbar | 5.600     |
+---+---------+--------------+-----------+
| 2 | LeBron  | James        | 5.511     |
+---+---------+--------------+-----------+
| 3 | Michael | Jordan       | 5.219     |
+---+---------+--------------+-----------+
| 4 | Tim     | Duncan       | 4.273     |
+---+---------+--------------+-----------+
| 5 | Bill    | Russell      | 4.066     |
+---+---------+--------------+-----------+
| 6 | Kobe    | Bryant       | 4.021     |
+---+---------+--------------+-----------+
```
Note that `sort_rows(col=2, key=float)` sorts the scores numerically — string sorting would put '10' before '2'.

Alternatively, `by_col` gives you the data column-wise as a dict, which is compatible with `pandas.DataFrame.from_dict()` if you'd rather process it that way:
```python
>>> tm.by_col(('album', 'year'), (('Fandango!', '1975'), ('Eliminator', '1983')))
{'album': ['Fandango!', 'Eliminator'], 'year': ['1975', '1983']}
```

## API summary
- `render(cols, rows=None, row_seps=True, title=None)` — the only function that produces a string. Without `rows`, returns a `rows -> str` function for pipelines.
- `pipe(value, *fns)` — thread a value through functions left to right.
- `sort_rows(col=0, key=None, reverse=False)`, `head(n=3)`, `tail(n=3)` — factories returning `rows -> rows` functions.
- `numbered(cols, rows)`, `title_case(cols, rows)` — pair transforms returning `(cols, rows)`.
- `by_col(cols, rows)` — column-wise dict view.
- `chop(rows)` — split rows into two halves.

## Changes from 0.1.x
0.2.0 is a breaking release. The formatted string is no longer the unit of manipulation — plain data is. `make_table` is now `render` (which computes per-column widths from the data, headers included, so `scaling` is gone); sorting moved out of rendering into `sort_rows`; `insert_row_numbers`, `capitalize_inputs`, `remove_seps`, and `insert_title` became `numbered`, `title_case`, the `row_seps` flag, and the `title` argument; `transform` became `by_col`. `deconstruct`, `maybe_table`, and `length` were removed — they existed to recover data from a rendered string, and you still have the data.
