sourcelibs/Format/src/Format.xtl
1⍝# Format: number formatting -- fixed decimals, thousands separators,
2⍝# percentages, aligned columns, text tables.
3⍝# Import it with an alias of your choice: "f:" u_se< "Format".
4⍝# Put libs/Format/src on XETAL_PATH ("just path"); the reference is libs/Format/docs.
5⍝# Names with l: are exported; those under h: are private to this file.
6⍝#
7⍝# X_eTaL's f_ormat has no width or precision yet (dyadic format is on
8⍝# its wish list): these build the text from the digits. A formatted
9⍝# list is a list of texts (Box Char), one per number, ready for
10⍝# f:c_olumn or f:t_able.
11
12ᵗ⁼u̲se< "Strings"
13"q:" u̲se< "Lists"
14
15digits ← ⎕D
16
17⍝## Fixed decimals
18
19⍝# The k decimal digits of a whole number d, leading zeros kept.
20ʰp̲added ← { k d → k = 0 ? ""◆ (1 + (k r̲eshape 10) e̲ncode d) s̲elect digits }
21
22⍝# One number with n decimals, rounded half away from zero.
23ʰo̲ne ← { n x →
24 s ← f̲loor 0.5 + (a̲bs f̲loat x) × f̲loat 10 ^ n
25 w ← s d̲iv 10 ^ n
26 sign ← ((x < 0) ∧ s > 0) r̲eplicate "-"
27 n = 0 ? sign c̲at f̲ormat w
28 sign c̲at (f̲ormat w) c̲at "." c̲at n ʰp̲added s m̲od 10 ^ n
29}
30
31⍝# n f_ixed v: each number of v with exactly n decimals (n >= 0).
32ˡf̲ixed ← { n v → '{ x → n ʰo̲ne x } m̲ap r̲avel v }
33
34⍝## Thousands and money
35
36⍝# A whole number >= 0 with a comma between each group of three digits:
37⍝# padded in front to a multiple of 3, cut into rows of 3, joined.
38ʰc̲ommas ← { w →
39 d ← f̲ormat w
40 p ← 3 × c̲eiling (f̲loat t̲ally d) ÷ 3.0
41 rows ← ((p d̲iv 3) c̲at 3) r̲eshape (n̲eg p) t̲ake d
42 ᵗt̲rimStart "," ᵗj̲oin '{ i → i s̲elect rows } m̲ap r̲ange p d̲iv 3
43}
44
45⍝# One number rounded to a whole number, its thousands separated.
46ʰg̲rouped ← { x →
47 w ← f̲loor 0.5 + f̲loat a̲bs x
48 (((x < 0) ∧ w > 0) r̲eplicate "-") c̲at ʰc̲ommas w
49}
50
51⍝# One number with n decimals and its thousands separated.
52ʰa̲mount1 ← { n x →
53 s ← f̲loor 0.5 + (a̲bs f̲loat x) × f̲loat 10 ^ n
54 w ← s d̲iv 10 ^ n
55 sign ← ((x < 0) ∧ s > 0) r̲eplicate "-"
56 n = 0 ? sign c̲at ʰc̲ommas w
57 sign c̲at (ʰc̲ommas w) c̲at "." c̲at n ʰp̲added s m̲od 10 ^ n
58}
59
60⍝# t_housands v: each number of v, rounded to a whole number, with its
61⍝# thousands separated by commas.
62ˡt̲housands ← { v → 'ʰg̲rouped m̲ap r̲avel v }
63
64⍝# n a_mount v: each number of v with n decimals and its thousands
65⍝# separated by commas (money: 2 f:a_mount 1324.69 is 1,324.69).
66ˡa̲mount ← { n v → '{ x → n ʰa̲mount1 x } m̲ap r̲avel v }
67
68⍝# n p_ercent v: each fraction of v as a percentage with n decimals.
69ˡp̲ercent ← { n v → '{ t → (d̲isclose t) c̲at "%" } m̲ap n ˡf̲ixed 100.0 × f̲loat v }
70
71⍝## Tables
72
73⍝# A list of texts as a character matrix, one per row, w wide.
74ʰr̲ows ← { w list → ((t̲ally list) c̲at w) r̲eshape q:r̲aze list }
75
76⍝# c_olumn list: the texts of a list right-aligned under each other (a
77⍝# character matrix), as numbers are set in a column.
78ˡc̲olumn ← { list →
79 w ← 'm̲ax r̲/ '{ t̲ally d̲isclose ⍵ } e̲ach list
80 w ʰr̲ows '{ t → w ᵗp̲adLeft d̲isclose t } m̲ap list
81}
82
83⍝# Whether a text reads as a number (set right-aligned in a table).
84ʰn̲umeric? ← { t → 0 = t̲ally t ? 0 = 1◆ '∧ r̲/ t m̲ember? ⎕D c̲at "-+.,%" }
85
86⍝# t_able cells: a matrix of texts (Box Char), its first row the
87⍝# header, as a character matrix: columns two spaces apart, each as wide
88⍝# as its widest text, a column of numbers (every cell under the header)
89⍝# right-aligned with its header, other columns left-aligned, a rule
90⍝# under the header.
91ˡt̲able ← { cells →
92 w ← 'm̲ax r̲/ '{ t̲ally d̲isclose ⍵ } e̲ach cells ⍝ each column's width
93 num ← '∧ r̲/ 1 d̲rop '{ ʰn̲umeric? d̲isclose ⍵ } e̲ach cells
94 ws ← r̲avel (s̲hape cells) r̲eshape w ⍝ each cell's width
95 ns ← r̲avel (s̲hape cells) r̲eshape num ⍝ each cell's alignment
96 cs ← r̲avel cells
97 p ← (s̲hape cells) r̲eshape '{ i →
98 n ← f̲irst i s̲elect ws
99 c ← d̲isclose i s̲elect cs
100 f̲irst i s̲elect ns ? n ᵗp̲adLeft c
101 n ᵗp̲adRight c
102 } m̲ap r̲ange t̲ally cs
103 rows ← '{ i → " " ᵗj̲oin i s̲elect p } m̲ap r̲ange t̲ally p
104 rule ← " " ᵗj̲oin '{ n → n r̲eshape "-" } m̲ap w
105 all ← (1 t̲ake rows) c̲at (e̲nclose rule) c̲at 1 d̲rop rows
106 (t̲ally rule) ʰr̲ows all
107}