-
Notifications
You must be signed in to change notification settings - Fork 0
/
barchart.js
96 lines (79 loc) · 3.12 KB
/
barchart.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
export function buildChart(labels, values, maxTicks, title = '') {
if (!Array.isArray(labels)) {
throw new Error(`The labels parameter provided is not an array: ${labels}`);
}
if (!Array.isArray(values)) {
throw new Error(`The values parameter provided is not an array: ${values}`);
}
if (labels.length !== values.length) {
throw new Error(`labels and values are of different lengths, ${labels.length} vs. ${values.length}`);
}
let maxValue = maxTicks;
let maxLabelLength = Number.NEGATIVE_INFINITY;
let maxValueLength = Number.NEGATIVE_INFINITY;
labels.forEach((label, i) => {
const value = values[i];
if (typeof label !== 'string') {
throw new Error(`Each of the labels provided must be strings, ${label} is not a string`);
}
if (typeof value !== 'number') {
throw new Error(`Each of the values provided must be numbers, ${value} is not a number`);
}
const labelLen = label.length;
const valueLen = ('' + value).length;
if (value > maxValue) {
maxValue = value;
}
if (labelLen > maxLabelLength) {
maxLabelLength = labelLen;
}
if (valueLen > maxValueLength) {
maxValueLength = valueLen;
}
});
let chartString = '';
if (title) {
const numTitleMarks = title.length + 2;
const titleMarksString = '-'.repeat(numTitleMarks);
chartString += `${titleMarksString}\n`;
chartString += `▏${title}▕\n`
chartString += `${titleMarksString}\n`;
}
labels.forEach((label, i) => {
const value = values[i];
const numLabelSpacersNeeded = maxLabelLength - label.length;
const numValueSpacersNeeded = maxValueLength - ('' + value).length;
const labelSpacerString = ' '.repeat(numLabelSpacersNeeded);
const valueSpacerString = ' '.repeat(numValueSpacersNeeded);
const tickString = getBarTickString(value, maxValue, maxTicks);
chartString += `${label}${labelSpacerString} | ${valueSpacerString}${value} ${tickString}\n`;
});
return chartString;
}
function getBarTickString(value, maxValue, maxTicks) {
const mappedValue = (value * maxTicks) / maxValue;
const numWholeTicks = Math.floor(mappedValue);
// Unicode block elements come in 1/8 increments, hence the 0.125 division below
// https://unicode-table.com/en/blocks/block-elements/
const numFractionalTicks = Math.round((mappedValue - numWholeTicks) / 0.125);
let fractionalTickString = '';
switch (numFractionalTicks) {
case 7:
fractionalTickString = '▉';
case 6:
fractionalTickString = '▊';
case 5:
fractionalTickString = '▋';
case 4:
fractionalTickString = '▌';
case 3:
fractionalTickString = '▍';
case 2:
fractionalTickString = '▎';
case 1:
fractionalTickString = '▏';
default:
fractionalTickString = '';
}
return `${'█'.repeat(numWholeTicks)}${fractionalTickString}`;
}