Skip to content

Commit 7cf40f4

Browse files
committed
Make colWidths more functional and declarative
Introduce the Either (Left | Right) types to remove data validation null / type checks.
1 parent 5978cce commit 7cf40f4

File tree

3 files changed

+49
-14
lines changed

3 files changed

+49
-14
lines changed

src/ascii-data-table.js

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import * as R from './functions'
22
import repeat from 'core-js/library/fn/string/repeat'
33

44
const len = (val) => typeof val === 'undefined' ? 0 : ('' + val).length
5+
const arrLen = (arr) => arr.length
6+
const arrMax = (arr) => R.apply(Math.max, arr)
7+
const matrixCol = (matrix) => (colNr) => R.pluck(colNr, matrix)
58
const padString = (character) => (width) => !width ? '' : repeat(character, width)
69
const spacePad = padString(' ')
7-
const stringifyRows = (rows) => {
8-
if (!Array.isArray(rows) || !rows.length) return []
9-
return rows.map((row) => row.map(JSON.stringify))
10-
}
10+
const stringifyArray = R.cMap(JSON.stringify)
11+
const stringifyRows = (rows) => R.EitherArray(rows).fold(() => null, R.cMap(stringifyArray))
1112
const insertColSeparators = (arr) => '│' + arr.join('│') + '│'
1213
const getTopSeparatorLine = (colWidths) => getSeparatorLine('═', '╒', '╤', '╕', colWidths)
1314
const getThickSeparatorLine = (colWidths) => getSeparatorLine('═', '╞', '╪', '╡', colWidths)
@@ -20,19 +21,23 @@ const getSeparatorLine = (horChar, leftChar, crossChar, rightChar, colWidths) =>
2021
}
2122

2223
const colWidths = (maxWidth, minWidth, input) => {
23-
if (!Array.isArray(input)) {
24-
return 0
25-
}
26-
return input[0].map((_, i) => {
27-
const tCol = R.pluck(i, input).map((col) => len(col))
28-
const measuredMax = Math.max(R.apply(Math.max, tCol), minWidth)
29-
return measuredMax > maxWidth && maxWidth > 0 ? maxWidth : measuredMax
30-
})
24+
const inputEither = R.EitherArray(input)
25+
const columnAtIndex = matrixCol(input)
26+
const normalizeWidth = (w) => Math.min(Math.max(w, minWidth), (maxWidth || Infinity))
27+
return inputEither
28+
.map((r) => R.head(r)[0]) // Grab title row
29+
.map(arrLen) // Get the number of columns
30+
.map(R.array) // Create a new array with same number of columns
31+
.map(R.cMap(columnAtIndex)) // Populate new array with columns from input
32+
.map(R.cMap(R.cMap(len))) // Measure the width of every column of every row
33+
.map(R.cMap(arrMax)) // Grab the max width of every column
34+
.map(R.cMap(normalizeWidth)) // Normalize width to be within limits
35+
.fold(() => [0], R.id) // default to 0
3136
}
3237

3338
const rowHeights = (maxWidth, input) => {
3439
return input.map((row) => {
35-
const maxLen = R.apply(Math.max, row.map((col) => len(col)))
40+
const maxLen = arrMax(row.map(len))
3641
const numLines = Math.ceil(maxLen / maxWidth)
3742
return numLines
3843
})
@@ -88,5 +93,5 @@ export default {
8893
serializeData: (rows) => stringifyRows(rows),
8994
tableFromSerializedData: (serializedRows, maxColumnWidth = 30) => main(serializedRows, maxColumnWidth),
9095
table: (rows, maxColumnWidth = 30) => main(stringifyRows(rows), maxColumnWidth),
91-
maxColumnWidth: (rows) => R.apply(Math.max, colWidths(0, 0, stringifyRows(rows)))
96+
maxColumnWidth: (rows) => arrMax(colWidths(0, 0, stringifyRows(rows)))
9297
}

src/functions.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
export const array = (len) => Array.apply(null, Array(len)).map((_, i) => i)
2+
export const id = (x) => x
13
export const pluck = (p, arr) => arr.map((o) => o[p])
24
export const apply = (fn, arr) => fn.apply(null, arr)
5+
export const cMap = (fn) => (arr) => arr.map(fn)
36
export const splitEvery = (w, a) => {
47
if (!a) return a
58
const tot = a.length
@@ -34,3 +37,28 @@ export const intersperse = (c, a) => {
3437
return all
3538
}, [])
3639
}
40+
41+
export const Either = (x) => x != null ? Right(x) : Left(x)
42+
export const EitherArray = (x) => Array.isArray(x) ? Right(x) : Left(x)
43+
44+
export const Right = (x) => ({
45+
chain: (f) => f(x),
46+
map: (f) => Right(f(x)),
47+
fold: (f, g) => g(x),
48+
inspect: () => `Right(${x})`,
49+
log: (str = '') => {
50+
console.log(str, 'Right', x)
51+
return Right(x)
52+
}
53+
})
54+
55+
export const Left = (x) => ({
56+
chain: (f) => Left(x),
57+
map: (f) => Left(x),
58+
fold: (f, g) => f(x),
59+
inspect: () => `Left(${x})`,
60+
log: (str = '') => {
61+
console.log(str, 'Left', x)
62+
return Left(x)
63+
}
64+
})

test/ascii-data-table-test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ describe('Ascii Tables', () => {
217217
})
218218

219219
it('should find the max column width', () => {
220+
checkColWidth(AsciiTable, null, 0)
221+
checkColWidth(AsciiTable, true, 0)
220222
checkColWidth(AsciiTable, [['x', 'y'], [{a: 'a', b: 'b'}, 'ab'], ['c', {d: 'd'}]],
221223
JSON.stringify({a: 'a', b: 'b'}).length
222224
)

0 commit comments

Comments
 (0)