Skip to content

Commit 4ed872e

Browse files
authored
fix: improve inspect/toString number formatter (#157)
1 parent 4b8e9b7 commit 4ed872e

File tree

5 files changed

+135
-17
lines changed

5 files changed

+135
-17
lines changed

matrix.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ export interface IToStringOptions {
110110
* @default `8`
111111
*/
112112
maxNumSize?: number;
113+
/**
114+
* Place minus signs in their own column.
115+
* @default `'auto'`
116+
*/
117+
padMinus?: true | false | 'auto';
113118
}
114119

115120
export abstract class AbstractMatrix {

src/__tests__/matrix/__snapshots__/inspect.js.snap

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,48 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`custom Node.js inspect function should properly format numbers 1`] = `
4+
"Matrix {
5+
[
6+
0 1 2 3 4 5 6 7 8 9
7+
0.123457 1.234568 12.34568 123.4568 1234.568 12345.68 123456.8 1234568 12345679 1.235e+8
8+
0.123457 0.012346 0.001235 1.235e-4 1.235e-5 1.235e-6 1.235e-7 1.235e-8 1.235e-9 1.23e-10
9+
0.123 1.23 12.3 123 1230 12300 123000 1230000 12300000 1.230e+8
10+
0.123 0.0123 0.00123 0.000123 1.230e-5 1.230e-6 1.23e-7 1.23e-8 1.230e-9 1.23e-10
11+
0.12 1.2 12 120 1200 12000 120000 1200000 12000000 1.200e+8
12+
0.12 0.012 0.0012 1.200e-4 0.000012 1.200e-6 1.2e-7 1.2e-8 1.2e-9 1.2e-10
13+
1.12 2.2 13 121 1201 12001 120001 1200001 12000001 1.200e+8
14+
1.12 1.012 1.0012 1.00012 1.000012 1.000001 1.000000 1.000000 1.000000 1.000000
15+
]
16+
rows: 9
17+
columns: 10
18+
}"
19+
`;
20+
21+
exports[`custom Node.js inspect function should properly format numbers 2`] = `
22+
"Matrix {
23+
[
24+
0 1 2 3 4 5 6 7 8 9
25+
0.12346 -1.23457 12.3457 -123.457 1234.57 -12345.7 123457 -1234568 1.23e+7 -1.23e+8
26+
-0.12346 0.01235 -0.00123 1.23e-4 -1.23e-5 1.23e-6 -1.23e-7 1.23e-8 -1.23e-9 1.2e-10
27+
0.123 -1.23 12.3 -123 1230 -12300 123000 -1230000 1.23e+7 -1.23e+8
28+
-0.123 0.0123 -0.00123 1.23e-4 -1.23e-5 1.23e-6 -1.23e-7 1.23e-8 -1.23e-9 1.2e-10
29+
0.12 -1.2 12 -120 1200 -12000 120000 -1200000 1.20e+7 -1.20e+8
30+
-0.12 0.012 -0.0012 1.20e-4 -1.20e-5 1.20e-6 -1.2e-7 1.2e-8 -1.2e-9 1.2e-10
31+
-0.12 -0.012 -0.0012 -1.20e-4 -1.20e-5 -1.20e-6 -1.2e-7 -1.2e-8 -1.2e-9 -1.2e-10
32+
1.12 2.2 13 121 1201 12001 120001 1200001 1.20e+7 1.20e+8
33+
1.12 1.012 1.0012 1.00012 1.00001 1.00000 1.00000 1.00000 1.00000 1.00000
34+
-1.12 -2.2 -13 -121 -1201 -12001 -120001 -1200001 -1.20e+7 -1.20e+8
35+
-1.12 -1.012 -1.0012 -1.00012 -1.00001 -1.00000 -1.00000 -1.00000 -1.00000 -1.00000
36+
]
37+
rows: 12
38+
columns: 10
39+
}"
40+
`;
41+
342
exports[`custom Node.js inspect function should work with a simple matrix 1`] = `
443
"Matrix {
544
[
6-
0 10 200
45+
0 10 200
746
-0.3 -0.44 -0.5555
847
]
948
rows: 2

src/__tests__/matrix/__snapshots__/toString.js.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ exports[`custom toString function should use clamp options 1`] = `
1414
exports[`custom toString function should work with a simple matrix 1`] = `
1515
"Matrix {
1616
[
17-
0 10 200
17+
0 10 200
1818
-0.3 -0.44 -0.5555
1919
]
2020
rows: 2

src/__tests__/matrix/inspect.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,40 @@ describe('custom Node.js inspect function', () => {
1313
),
1414
).toMatchSnapshot();
1515
});
16+
it('should properly format numbers', () => {
17+
const a10 = Array.from({ length: 10 }, (_, i) => i);
18+
expect(
19+
inspect(
20+
new Matrix([
21+
a10,
22+
a10.map((e) => 0.123456789 * 10 ** e),
23+
a10.map((e) => 0.123456789 / 10 ** e),
24+
a10.map((e) => 0.123 * 10 ** e),
25+
a10.map((e) => 0.123 / 10 ** e),
26+
a10.map((e) => 0.12 * 10 ** e),
27+
a10.map((e) => 0.12 / 10 ** e),
28+
a10.map((e) => 1 + 0.12 * 10 ** e),
29+
a10.map((e) => 1 + 0.12 / 10 ** e),
30+
]),
31+
),
32+
).toMatchSnapshot();
33+
expect(
34+
inspect(
35+
new Matrix([
36+
a10,
37+
a10.map((e) => (-1) ** e * 0.123456789 * 10 ** e),
38+
a10.map((e) => ((-1) ** e * -0.123456789) / 10 ** e),
39+
a10.map((e) => (-1) ** e * 0.123 * 10 ** e),
40+
a10.map((e) => ((-1) ** e * -0.123) / 10 ** e),
41+
a10.map((e) => (-1) ** e * 0.12 * 10 ** e),
42+
a10.map((e) => ((-1) ** e * -0.12) / 10 ** e),
43+
a10.map((e) => -0.12 / 10 ** e),
44+
a10.map((e) => 1 + 0.12 * 10 ** e),
45+
a10.map((e) => 1 + 0.12 / 10 ** e),
46+
a10.map((e) => -1 - 0.12 * 10 ** e),
47+
a10.map((e) => -1 - 0.12 / 10 ** e),
48+
]),
49+
),
50+
).toMatchSnapshot();
51+
});
1652
});

src/inspect.js

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,43 @@ export function inspectMatrix() {
66
}
77

88
export function inspectMatrixWithOptions(matrix, options = {}) {
9-
const { maxRows = 15, maxColumns = 10, maxNumSize = 8 } = options;
9+
const {
10+
maxRows = 15,
11+
maxColumns = 10,
12+
maxNumSize = 8,
13+
padMinus = 'auto',
14+
} = options;
1015
return `${matrix.constructor.name} {
1116
${indent}[
12-
${indentData}${inspectData(matrix, maxRows, maxColumns, maxNumSize)}
17+
${indentData}${inspectData(matrix, maxRows, maxColumns, maxNumSize, padMinus)}
1318
${indent}]
1419
${indent}rows: ${matrix.rows}
1520
${indent}columns: ${matrix.columns}
1621
}`;
1722
}
1823

19-
function inspectData(matrix, maxRows, maxColumns, maxNumSize) {
24+
function inspectData(matrix, maxRows, maxColumns, maxNumSize, padMinus) {
2025
const { rows, columns } = matrix;
2126
const maxI = Math.min(rows, maxRows);
2227
const maxJ = Math.min(columns, maxColumns);
2328
const result = [];
29+
30+
if (padMinus === 'auto') {
31+
padMinus = false;
32+
loop: for (let i = 0; i < maxI; i++) {
33+
for (let j = 0; j < maxJ; j++) {
34+
if (matrix.get(i, j) < 0) {
35+
padMinus = true;
36+
break loop;
37+
}
38+
}
39+
}
40+
}
41+
2442
for (let i = 0; i < maxI; i++) {
2543
let line = [];
2644
for (let j = 0; j < maxJ; j++) {
27-
line.push(formatNumber(matrix.get(i, j), maxNumSize));
45+
line.push(formatNumber(matrix.get(i, j), maxNumSize, padMinus));
2846
}
2947
result.push(`${line.join(' ')}`);
3048
}
@@ -37,17 +55,37 @@ function inspectData(matrix, maxRows, maxColumns, maxNumSize) {
3755
return result.join(`\n${indentData}`);
3856
}
3957

40-
function formatNumber(num, maxNumSize) {
41-
const numStr = String(num);
42-
if (numStr.length <= maxNumSize) {
43-
return numStr.padEnd(maxNumSize, ' ');
58+
function formatNumber(num, maxNumSize, padMinus) {
59+
return (
60+
num >= 0 && padMinus
61+
? ` ${formatNumber2(num, maxNumSize - 1)}`
62+
: formatNumber2(num, maxNumSize)
63+
).padEnd(maxNumSize);
64+
}
65+
66+
function formatNumber2(num, len) {
67+
// small.length numbers should be as is
68+
let str = num.toString();
69+
if (str.length <= len) return str;
70+
71+
// (7)'0.00123' is better then (7)'1.23e-2'
72+
// (8)'0.000123' is worse then (7)'1.23e-3',
73+
let fix = num.toFixed(len);
74+
if (fix.length > len) {
75+
fix = num.toFixed(Math.max(0, len - (fix.length - len)));
4476
}
45-
const precise = num.toPrecision(maxNumSize - 2);
46-
if (precise.length <= maxNumSize) {
47-
return precise;
77+
if (
78+
fix.length <= len &&
79+
!fix.startsWith('0.000') &&
80+
!fix.startsWith('-0.000')
81+
) {
82+
return fix;
83+
}
84+
85+
// well, if it's still too long the user should've used longer numbers
86+
let exp = num.toExponential(len);
87+
if (exp.length > len) {
88+
exp = num.toExponential(Math.max(0, len - (exp.length - len)));
4889
}
49-
const exponential = num.toExponential(maxNumSize - 2);
50-
const eIndex = exponential.indexOf('e');
51-
const e = exponential.slice(eIndex);
52-
return exponential.slice(0, maxNumSize - e.length) + e;
90+
return exp.slice(0);
5391
}

0 commit comments

Comments
 (0)