jupyter-ijavascript-utils 1.14.2 → 1.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +4 -1
- package/README.md +2 -1
- package/package.json +2 -1
- package/src/TableGenerator.js +9 -26
- package/src/format.js +194 -4
- package/src/object.js +171 -9
package/DOCS.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Overview
|
|
2
2
|
|
|
3
|
-
This is a
|
|
3
|
+
This is a library to help people that understand JavaScript
|
|
4
|
+
to leverage for using Jupyter with the iJavaScript kernel
|
|
5
|
+
as a way to load and explore data, and ultimately tell compelling stories with visuals.
|
|
4
6
|
|
|
5
7
|
Jupyter is a way to programmatically explore a subject and interleave text and markdown to make Data Driven Documents.
|
|
6
8
|
|
|
@@ -44,6 +46,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
|
|
|
44
46
|
|
|
45
47
|
## What's New
|
|
46
48
|
|
|
49
|
+
* 1.15 - provide {@link module:object.formatProperties|object.formatProperties} - as a way to quickly convert to string, number, etc.
|
|
47
50
|
* 1.14 - provide {@link module:object.mapProperties|object.mapProperties()} and {@link module:format.compactNumber|format.compactNumber()}
|
|
48
51
|
* 1.13 - provide {@link module:random|utils.random()} to genrate random values
|
|
49
52
|
* 1.12 - provide `utils.table(...)` instead of `new utils.TableGenerator(...)`
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</a>
|
|
8
8
|
<img src="https://img.shields.io/badge/Coverage-98-green" />
|
|
9
9
|
<a href="https://github.com/paulroth3d/jupyter-ijavascript-utils" alt="npm">
|
|
10
|
-
<img src="https://img.shields.io/badge/npm-%5E1.
|
|
10
|
+
<img src="https://img.shields.io/badge/npm-%5E1.15-red" />
|
|
11
11
|
</a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
@@ -46,6 +46,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
|
|
|
46
46
|
|
|
47
47
|
# What's New
|
|
48
48
|
|
|
49
|
+
* 1.15 - provide object.formatProperties - as a way to quickly convert to string, number, etc.
|
|
49
50
|
* 1.14 - provide format.compactNumber and object.mapProperties
|
|
50
51
|
* 1.13 - provide utils.random() to genrate random values
|
|
51
52
|
* 1.12 - provide `utils.table(...)` instead of `new utils.TableGenerator(...)`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyter-ijavascript-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.2",
|
|
4
4
|
"description": "Utilities for working with iJavaScript - a Jupyter Kernel",
|
|
5
5
|
"homepage": "https://jupyter-ijavascript-utils.onrender.com/",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"test:debug": "TZ=UTC node --inspect-brk node_modules/jest/bin/jest.js --runInBand",
|
|
14
14
|
"test:coverage": "TZ=UTC jest src --collectCoverage",
|
|
15
15
|
"test:watch:coverage": "TZ=UTC jest src --watch --collectCoverage",
|
|
16
|
+
"docs": "npm run doc",
|
|
16
17
|
"doc": "npm run prep:docdash && node_modules/.bin/jsdoc -c ./jsdoc.json -u ./tutorials ./DOCS.md",
|
|
17
18
|
"prep:docdash": "cp docResources/docdash/layout.tmpl node_modules/docdash/tmpl/layout.tmpl && rm -rf docResources/notebooks/node_modules",
|
|
18
19
|
"open:coverage": "open ./coverage/lcov-report/index.html",
|
package/src/TableGenerator.js
CHANGED
|
@@ -18,6 +18,10 @@ const IJSUtils = require('./ijs');
|
|
|
18
18
|
|
|
19
19
|
const ArrayUtils = require('./array');
|
|
20
20
|
|
|
21
|
+
const FormatUtils = require('./format');
|
|
22
|
+
|
|
23
|
+
const ObjectUtils = require('./object');
|
|
24
|
+
|
|
21
25
|
const { createSort } = require('./array');
|
|
22
26
|
|
|
23
27
|
/**
|
|
@@ -613,27 +617,11 @@ class TableGenerator {
|
|
|
613
617
|
return this;
|
|
614
618
|
}
|
|
615
619
|
|
|
620
|
+
const cleanedFormatter = FormatUtils.prepareFormatterObject(obj);
|
|
621
|
+
|
|
616
622
|
const fnMap = new Map();
|
|
617
|
-
Object.getOwnPropertyNames(
|
|
618
|
-
|
|
619
|
-
let fn;
|
|
620
|
-
const str = obj[key].toLowerCase();
|
|
621
|
-
if (str === 'string') {
|
|
622
|
-
fn = (val) => String(val);
|
|
623
|
-
} else if (str === 'number') {
|
|
624
|
-
fn = (val) => Number(val);
|
|
625
|
-
} else if (str === 'boolean') {
|
|
626
|
-
fn = (val) => val ? 'true' : 'false';
|
|
627
|
-
} else {
|
|
628
|
-
throw Error(`TableGenerator.format: property ${key} formatter of ${str} is unsupported. Only (String, Number, Boolean) are supported`);
|
|
629
|
-
}
|
|
630
|
-
fnMap.set(key, fn);
|
|
631
|
-
} else {
|
|
632
|
-
if ((typeof obj[key]) !== 'function') {
|
|
633
|
-
throw (Error(`Formatter properties must be functions. [${key}]`));
|
|
634
|
-
}
|
|
635
|
-
fnMap.set(key, obj[key]);
|
|
636
|
-
}
|
|
623
|
+
Object.getOwnPropertyNames(cleanedFormatter).forEach((key) => {
|
|
624
|
+
fnMap.set(key, cleanedFormatter[key]);
|
|
637
625
|
});
|
|
638
626
|
|
|
639
627
|
this.#formatterFn = ({ value, property }) => fnMap.has(property)
|
|
@@ -1016,12 +1004,7 @@ class TableGenerator {
|
|
|
1016
1004
|
}
|
|
1017
1005
|
|
|
1018
1006
|
//-- determine the columns to use
|
|
1019
|
-
|
|
1020
|
-
const firstRow = cleanCollection.length > 0
|
|
1021
|
-
? cleanCollection[0]
|
|
1022
|
-
: {};
|
|
1023
|
-
|
|
1024
|
-
let keys = this.#columns || Object.keys(firstRow);
|
|
1007
|
+
let keys = this.#columns || ObjectUtils.keys(cleanCollection);// Object.keys(firstRow);
|
|
1025
1008
|
keys = keys.filter((key) => this.#columnsToExclude.indexOf(key) === -1);
|
|
1026
1009
|
|
|
1027
1010
|
//-- identify the formatter to use
|
package/src/format.js
CHANGED
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
* * Identifying Time Periods
|
|
23
23
|
* * {@link module:format.timePeriod|format.timePeriod} - Converts a time to a time period, very helpful for animations
|
|
24
24
|
* * {@link module:format.timePeriodPercent|format.timePeriodPercent} - Determines the percent complete of the current time period
|
|
25
|
+
* * Converting values safely
|
|
26
|
+
* * {@link module:format.safeConvertString|format.safeConvertString} - converts a value to string, or uses a default for any error
|
|
27
|
+
* * {@link module:format.safeConvertFloat|format.safeConvertFloat} - converts a value to a Number (123.4), or uses a default for any error or NaN
|
|
28
|
+
* * {@link module:format.safeConvertInteger|format.safeConvertInteger} - converts a value to a Number (123), or uses a default for any error or NaN
|
|
29
|
+
* * {@link module:format.safeConvertBoolean|format.safeConvertBoolean} - converts a value to a boolean
|
|
25
30
|
*
|
|
26
31
|
* @module format
|
|
27
32
|
* @exports format
|
|
@@ -307,11 +312,19 @@ module.exports.millisecondDuration = function millisecondDuration(milliseconds)
|
|
|
307
312
|
* format.ellipsify('longName', 8) // 'longName' (as maxLen is 8)
|
|
308
313
|
* format.ellipsify('longName', 4) // 'long…' (as str is longer than maxLen)
|
|
309
314
|
*/
|
|
310
|
-
module.exports.ellipsify = function ellipsify(str, maxLen
|
|
311
|
-
|
|
312
|
-
|
|
315
|
+
module.exports.ellipsify = function ellipsify(str, maxLen) {
|
|
316
|
+
const cleanStr = !str
|
|
317
|
+
? ''
|
|
318
|
+
: typeof str === 'string'
|
|
319
|
+
? str
|
|
320
|
+
: JSON.stringify(str);
|
|
321
|
+
|
|
322
|
+
const cleanLen = maxLen > 0 ? maxLen : 50;
|
|
323
|
+
|
|
324
|
+
if (cleanStr.length > cleanLen) {
|
|
325
|
+
return `${cleanStr.substring(0, cleanLen)}…`;
|
|
313
326
|
}
|
|
314
|
-
return
|
|
327
|
+
return cleanStr;
|
|
315
328
|
};
|
|
316
329
|
|
|
317
330
|
/**
|
|
@@ -632,3 +645,180 @@ module.exports.compactNumber = function compactNumber(num, digits = 0) {
|
|
|
632
645
|
|
|
633
646
|
return (num / siValue).toFixed(digits) + siKey;
|
|
634
647
|
};
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Converts a value to a String, <br />
|
|
651
|
+
* Or returns `otherwise` if any exceptions are found.
|
|
652
|
+
*
|
|
653
|
+
* @param {any} val - value to convert
|
|
654
|
+
* @param {any} otherwise - value to use if any exceptions are caught
|
|
655
|
+
* @returns {String} - `String(val)`
|
|
656
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive|toPrimitive}
|
|
657
|
+
* @returns {String}
|
|
658
|
+
* @example
|
|
659
|
+
* utils.format.safeConvertString(23); // '23'
|
|
660
|
+
*
|
|
661
|
+
* const customObj = {
|
|
662
|
+
* toString: () => `String Value`
|
|
663
|
+
* };
|
|
664
|
+
* utils.format.safeConvertString(customObj); // 'String Value'
|
|
665
|
+
*/
|
|
666
|
+
module.exports.safeConvertString = function safeConvertString(val, otherwise = null) {
|
|
667
|
+
try {
|
|
668
|
+
return String(val);
|
|
669
|
+
} catch (err) {
|
|
670
|
+
return otherwise;
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Converts a value to a Floating Number, <br />
|
|
676
|
+
* Or returns `otherwise` if any exceptions are found or value is NaN
|
|
677
|
+
*
|
|
678
|
+
* @param {any} val - value to convert
|
|
679
|
+
* @param {any} otherwise - value to use if any exceptions are caught
|
|
680
|
+
* @returns {Number}
|
|
681
|
+
* @example
|
|
682
|
+
* utils.format.safeConvertFloat('23.1'); // 23.1
|
|
683
|
+
* utils.format.safeConvertFloat('not a number', -1); // -1
|
|
684
|
+
*/
|
|
685
|
+
module.exports.safeConvertFloat = function safeConvertFloat(val, otherwise = NaN) {
|
|
686
|
+
if (typeof val === 'string') {
|
|
687
|
+
//-- replace the variable in memory to minimize garbage collection.
|
|
688
|
+
// eslint-disable-next-line no-param-reassign
|
|
689
|
+
val = val.replace(/[^0-9.]/g, '');
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
try {
|
|
693
|
+
const result = Number.parseFloat(val);
|
|
694
|
+
if (Number.isNaN(result)) {
|
|
695
|
+
return otherwise;
|
|
696
|
+
}
|
|
697
|
+
return result;
|
|
698
|
+
} catch (err) {
|
|
699
|
+
//-- cannot reliably get the exception to be thrown so cannot test this line
|
|
700
|
+
/* istanbul ignore next */
|
|
701
|
+
return otherwise;
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Converts a value to a Floating Number, <br />
|
|
707
|
+
* Or returns `otherwise` if any exceptions are found or value is Not a Number.
|
|
708
|
+
*
|
|
709
|
+
* @param {any} val - value to convert
|
|
710
|
+
* @param {any} otherwise - value to use if any exceptions are caught
|
|
711
|
+
* @param {Number} [radix = 10] - radix to use in converting the string
|
|
712
|
+
* @returns {Number}
|
|
713
|
+
* @example
|
|
714
|
+
* utils.format.safeConvertFloat('23'); // 23
|
|
715
|
+
* utils.format.safeConvertFloat('not a number', -1); // -1
|
|
716
|
+
*/
|
|
717
|
+
module.exports.safeConvertInteger = function safeConvertInteger(val, otherwise = NaN, radix = 10) {
|
|
718
|
+
if (typeof val === 'string') {
|
|
719
|
+
//-- replace the variable in memory to minimize garbage collection.
|
|
720
|
+
// eslint-disable-next-line no-param-reassign
|
|
721
|
+
val = val.replace(/[^0-9.]/g, '');
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
try {
|
|
725
|
+
const result = Number.parseInt(val, radix);
|
|
726
|
+
if (Number.isNaN(result)) {
|
|
727
|
+
return otherwise;
|
|
728
|
+
}
|
|
729
|
+
return result;
|
|
730
|
+
} catch (err) {
|
|
731
|
+
//-- cannot reliably get the exception to be thrown so cannot test this line
|
|
732
|
+
/* istanbul ignore next */
|
|
733
|
+
return otherwise;
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Converts a value to boolean.
|
|
739
|
+
*
|
|
740
|
+
* Note this uses the standard JavaScript `truthy` conversion,
|
|
741
|
+
* but with special exceptions for strings: only 'true', 'yes', '1' are considered true.
|
|
742
|
+
*
|
|
743
|
+
* @param {any} val - value to be converted
|
|
744
|
+
* @returns {Boolean}
|
|
745
|
+
* @example
|
|
746
|
+
* utils.format.safeConvertBoolean(1); // true
|
|
747
|
+
* utils.format.safeConvertBoolean({ pojo: true }); // true
|
|
748
|
+
* utils.format.safeConvertBoolean('TruE'); // true - case insensitive
|
|
749
|
+
* utils.format.safeConvertBoolean('YeS'); // true - case insensitive
|
|
750
|
+
* utils.format.safeConvertBoolean('1'); // true
|
|
751
|
+
*
|
|
752
|
+
* utils.format.safeConvertBoolean(0); // false
|
|
753
|
+
* utils.format.safeConvertBoolean(null); // false
|
|
754
|
+
* utils.format.safeConvertBoolean('false'); // false
|
|
755
|
+
* utils.format.safeConvertBoolean('No'); // false
|
|
756
|
+
* utils.format.safeConvertBoolean('0'); // false
|
|
757
|
+
*/
|
|
758
|
+
module.exports.safeConvertBoolean = function safeConvertBoolean(val) {
|
|
759
|
+
if (typeof val === 'string') {
|
|
760
|
+
const valUpper = val.toUpperCase();
|
|
761
|
+
return valUpper === 'TRUE' || valUpper === 'YES' || valUpper === '1';
|
|
762
|
+
}
|
|
763
|
+
return val ? true : false;
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
module.exports.parseCommand = function parseCommand(commandStr) {
|
|
767
|
+
if (!commandStr || commandStr.indexOf('(') < 0) {
|
|
768
|
+
return [commandStr];
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const match = commandStr.match(/^([a-zA-Z].+)[(](.*)[)]$/i);
|
|
772
|
+
let result = [];
|
|
773
|
+
|
|
774
|
+
if (match) {
|
|
775
|
+
const commandArgs = (match[2] || '').split(',').map((s) => s.trim()).filter((v) => v !== '');
|
|
776
|
+
result = [
|
|
777
|
+
match[1].trim(),
|
|
778
|
+
commandArgs
|
|
779
|
+
];
|
|
780
|
+
} else {
|
|
781
|
+
result = [commandStr];
|
|
782
|
+
}
|
|
783
|
+
return result;
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
module.exports.prepareFormatterObject = function prepareFormatterObject(formatterObject) {
|
|
787
|
+
//-- @TODO: find way to reliably say that the propertyTranslation is an object
|
|
788
|
+
// propertyTranslations.constructor.name !== 'Object'
|
|
789
|
+
if (!formatterObject) {
|
|
790
|
+
throw Error(['ObjectUtils.formatProperties(collection, propertyTranslations): propertyTranslations must be an object, ',
|
|
791
|
+
'with the properties matching those to be formatted, and values as functions returning the new value'].join(''));
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const translationKeys = Array.from(Object.keys(formatterObject));
|
|
795
|
+
|
|
796
|
+
const result = ({ ...formatterObject });
|
|
797
|
+
|
|
798
|
+
translationKeys.forEach((key) => {
|
|
799
|
+
const translationVal = formatterObject[key];
|
|
800
|
+
const translationValType = typeof translationVal;
|
|
801
|
+
if (translationValType === 'function') {
|
|
802
|
+
//-- do nothing
|
|
803
|
+
} else if (translationValType === 'string') {
|
|
804
|
+
const [command, args = []] = FormatUtils.parseCommand(translationVal);
|
|
805
|
+
if (command === 'string') {
|
|
806
|
+
result[key] = FormatUtils.safeConvertString;
|
|
807
|
+
} else if (command === 'ellipsify' || command === 'elipsify' || command === 'ellipsis' || command === 'elipsis') {
|
|
808
|
+
result[key] = (str) => FormatUtils.ellipsify(str, args[0]);
|
|
809
|
+
} else if (command === 'number' || command === 'float') {
|
|
810
|
+
result[key] = FormatUtils.safeConvertFloat;
|
|
811
|
+
} else if (command === 'int' || command === 'integer') {
|
|
812
|
+
result[key] = FormatUtils.safeConvertInteger;
|
|
813
|
+
} else if (command === 'bool' || command === 'boolean') {
|
|
814
|
+
result[key] = FormatUtils.safeConvertBoolean;
|
|
815
|
+
} else {
|
|
816
|
+
result[key] = () => translationVal;
|
|
817
|
+
}
|
|
818
|
+
} else {
|
|
819
|
+
result[key] = () => translationVal;
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
return result;
|
|
824
|
+
};
|
package/src/object.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const schemaGenerator = require('generate-schema');
|
|
4
4
|
|
|
5
|
+
const FormatUtils = require('./format');
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Utility for working with and massaging javascript objects.
|
|
7
9
|
*
|
|
@@ -18,6 +20,7 @@ const schemaGenerator = require('generate-schema');
|
|
|
18
20
|
* * {@link module:object.selectObjectProperties|selectObjectProperties()} - keep only specific properties
|
|
19
21
|
* * {@link module:object.filterObjectProperties|filterObjectProperties()} - remove specific properties
|
|
20
22
|
* * {@link module:object.mapProperties|mapProperties(collection, fn, ...properties)} - map multiple properties at once (like parseInt, or toString)
|
|
23
|
+
* * {@link module:object.formatProperties|formatProperties(collection, propertyTranslation)} - map specific properties (ex: toString, toNumber, etc)
|
|
21
24
|
* * Fetch child properties from related objects
|
|
22
25
|
* * {@link module:object.fetchObjectProperty|fetchObjectProperty(object, string)} - use dot notation to bring a child property onto a parent
|
|
23
26
|
* * {@link module:object.fetchObjectProperties|fetchObjectProperties(object, string[])} - use dot notation to bring multiple child properties onto a parent
|
|
@@ -25,7 +28,8 @@ const schemaGenerator = require('generate-schema');
|
|
|
25
28
|
* * {@link module:object.joinProperties|join(array, index, map, ...fields)} - join a collection, and copy properties over from the mapped object.
|
|
26
29
|
* * {@link module:object.propertyFromList|propertyFromList(array, propertyName)} - fetches a specific property from all objects in a list
|
|
27
30
|
* * Rename properties
|
|
28
|
-
* * {@link module:object.cleanProperties|cleanProperties()} - correct inaccessible property names in a list of objects
|
|
31
|
+
* * {@link module:object.cleanProperties|cleanProperties()} - correct inaccessible property names in a list of objects - in place
|
|
32
|
+
* * * {@link module:object.cleanProperties2|cleanProperties2()} - correct inaccessible property names in a list of objects - on a cloned list
|
|
29
33
|
* * {@link module:object.cleanPropertyNames|cleanPropertyNames()} - create a translation of inaccessible names to accessible ones
|
|
30
34
|
* * {@link module:object.cleanPropertyName|cleanPropertyName()} - create a translation of a specific property name to be accessible.
|
|
31
35
|
* * {@link module:object.renameProperties|renameProperties()} - Use a translation from old property names to new ones
|
|
@@ -146,7 +150,8 @@ module.exports.objAssignEntities = function objAssignEntities(obj, entities) {
|
|
|
146
150
|
* Runs a map over a collection, and adds properties the the objects.
|
|
147
151
|
*
|
|
148
152
|
* @param {Object | Array<Object>} objCollection - object or collection of objects to augment
|
|
149
|
-
* @param {Function} mappingFn - (record) => {Object} mapping function
|
|
153
|
+
* @param {Function | Object} mappingFn - (record) => {Object} mapping function <br />
|
|
154
|
+
* or object with properties to create
|
|
150
155
|
* @param {Boolean} [inPlace=false] - whether to update the collection in place (true) or cloned (false)
|
|
151
156
|
* @returns {Array<Object>} - collection of records with the fields merged
|
|
152
157
|
* @example
|
|
@@ -227,7 +232,11 @@ module.exports.keys = function keys(objOrArray = {}) {
|
|
|
227
232
|
};
|
|
228
233
|
|
|
229
234
|
/**
|
|
230
|
-
* Cleans all the properties of the array of objects
|
|
235
|
+
* Cleans all the properties of the array of objects in place (does not make Copies)
|
|
236
|
+
*
|
|
237
|
+
* **NOTE: This is faster than {@link module:ObjectUtils.cleanProperties2|cleanProperties2},
|
|
238
|
+
* but the standard order of the properties (using Object.keys) will be altered.**
|
|
239
|
+
*
|
|
231
240
|
* @param {Object[]} objectsToBeCleaned -
|
|
232
241
|
* @return {Object[]} - cleaned objects
|
|
233
242
|
*/
|
|
@@ -238,21 +247,98 @@ module.exports.cleanProperties = function cleanProperties(objectsToBeCleaned) {
|
|
|
238
247
|
);
|
|
239
248
|
};
|
|
240
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Labels and Values from {@link module:object.cleanProperties2|object.cleanProperties2}
|
|
252
|
+
*
|
|
253
|
+
* ```
|
|
254
|
+
* {
|
|
255
|
+
* labels: { date: 'date', kind: 'kind', num: 'num' },
|
|
256
|
+
* values: [
|
|
257
|
+
* { date: ' 2021-07-11T22:23:07+0100', kind: ' s', num: '192' },
|
|
258
|
+
* { date: ' 2021-07-09T19:54:48+0100', kind: ' c', num: '190' },
|
|
259
|
+
* { date: ' 2021-07-08T17:00:32+0100', kind: ' s', num: '190' }
|
|
260
|
+
* ]
|
|
261
|
+
* };
|
|
262
|
+
* ```
|
|
263
|
+
*
|
|
264
|
+
* @typedef {Object} CleanedProperties
|
|
265
|
+
* @property {Object} labels - an object with translations of the fields and labels
|
|
266
|
+
* @property {String} labels.property - for each translated property, stores the original property name
|
|
267
|
+
* @property {Object[]} values - cleaned values
|
|
268
|
+
|
|
269
|
+
*/
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Cleans properties on clones of objects.
|
|
273
|
+
*
|
|
274
|
+
* Additionally, this returns a mapping of what the properties used to be named,
|
|
275
|
+
* as this can be helpful for rendering out tables.
|
|
276
|
+
*
|
|
277
|
+
* @param {Object[]} objectsToBeCleaned - collection of objects to be cleaned
|
|
278
|
+
* @returns {CleanedProperties} - { labels: Object - propertyName:originalProperty, values: cleaned collection }
|
|
279
|
+
* @see {@link module:object~CleanedProperties}
|
|
280
|
+
* @example
|
|
281
|
+
const badData = [
|
|
282
|
+
{ '"name"': 'john', num: '192', ' kind': ' s', '1st date': ' 2021-07-11T22:23:07+0100' },
|
|
283
|
+
{ '"name"': 'jane', num: '190', ' kind': ' c', '1st date': ' 2021-07-09T19:54:48+0100' },
|
|
284
|
+
{ '"name"': 'ringo', num: '190', ' kind': ' s', '1st date': ' 2021-07-08T17:00:32+0100' }
|
|
285
|
+
];
|
|
286
|
+
const cleaned = objectUtils.cleanProperties2(badData);
|
|
287
|
+
// {
|
|
288
|
+
// labels: { 1st_date: '1st date', kind: 'kind', num: 'num' },
|
|
289
|
+
// values: [
|
|
290
|
+
// { name: 'john', num: '192', kind: ' s', '1st_date': ' 2021-07-11T22:23:07+0100' },
|
|
291
|
+
// { name: 'jane', num: '190', kind: ' c', '1st_date': ' 2021-07-09T19:54:48+0100' },
|
|
292
|
+
// { name: 'ringo', num: '190', kind: ' s', '1st_date': ' 2021-07-08T17:00:32+0100' }
|
|
293
|
+
// ]
|
|
294
|
+
// }
|
|
295
|
+
*/
|
|
296
|
+
module.exports.cleanProperties2 = function cleanProperties2(objectsToBeCleaned) {
|
|
297
|
+
const cleanedPropertyNames = ObjectUtils.cleanPropertyNames(objectsToBeCleaned);
|
|
298
|
+
const keys = ObjectUtils.keys(cleanedPropertyNames);
|
|
299
|
+
|
|
300
|
+
const translation = keys.reduce((result, key) => ObjectUtils
|
|
301
|
+
.objAssign(result, cleanedPropertyNames[key], ObjectUtils.lightlyCleanProperty(key)), {});
|
|
302
|
+
|
|
303
|
+
const values = (objectsToBeCleaned || [])
|
|
304
|
+
.map((obj) => keys.reduce(
|
|
305
|
+
(result, key) => ObjectUtils.objAssign(result, cleanedPropertyNames[key], obj[key]),
|
|
306
|
+
{}
|
|
307
|
+
));
|
|
308
|
+
|
|
309
|
+
return ({ labels: translation, values });
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* cleans properties so they are still legible
|
|
314
|
+
* @private
|
|
315
|
+
* @param {String} propertyName - property name to be cleaned
|
|
316
|
+
* @returns {String}
|
|
317
|
+
*/
|
|
318
|
+
module.exports.lightlyCleanProperty = function lightlyCleanProperty(propertyName) {
|
|
319
|
+
//-- assume property name is a string
|
|
320
|
+
return propertyName.trim()
|
|
321
|
+
.replace(/^["']/, '')
|
|
322
|
+
.replace(/['"]$/, '');
|
|
323
|
+
};
|
|
324
|
+
|
|
241
325
|
/**
|
|
242
326
|
* Cleans the list of object keys - likely from a CSV
|
|
243
327
|
* @param {(Object| String[])} objectKeys -
|
|
244
328
|
* @return {Object} - object with key:value as original:new
|
|
245
329
|
*/
|
|
246
|
-
module.exports.cleanPropertyNames = function cleanPropertyNames(
|
|
330
|
+
module.exports.cleanPropertyNames = function cleanPropertyNames(target) {
|
|
247
331
|
let originalKeys;
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
332
|
+
if (!target) {
|
|
333
|
+
return {};
|
|
334
|
+
} else if (Array.isArray(target)) {
|
|
335
|
+
if ((typeof target[0]) === 'string') {
|
|
336
|
+
originalKeys = target;
|
|
251
337
|
} else {
|
|
252
|
-
originalKeys = ObjectUtils.keys(
|
|
338
|
+
originalKeys = ObjectUtils.keys(target);
|
|
253
339
|
}
|
|
254
340
|
} else {
|
|
255
|
-
originalKeys = Object.keys(
|
|
341
|
+
originalKeys = Object.keys(target);
|
|
256
342
|
}
|
|
257
343
|
|
|
258
344
|
const result = {};
|
|
@@ -439,6 +525,82 @@ module.exports.fetchObjectProperty = function fetchObjectProperty(obj, propertyA
|
|
|
439
525
|
}, obj);
|
|
440
526
|
};
|
|
441
527
|
|
|
528
|
+
/**
|
|
529
|
+
* Translates specific properties to a new value on an object, or collection of objects.
|
|
530
|
+
*
|
|
531
|
+
* The properties defined in the `propertyTranslations` argument is then the property to be updated. (All other properties remain the same)
|
|
532
|
+
*
|
|
533
|
+
* You can either provide a function accepting the current value and returning the new value (any) => any
|
|
534
|
+
*
|
|
535
|
+
* Or you can provide one of the common shorthands:
|
|
536
|
+
*
|
|
537
|
+
* * 'string'
|
|
538
|
+
* * 'float' or 'number'
|
|
539
|
+
* * 'int' or 'integer'
|
|
540
|
+
* * 'boolean'
|
|
541
|
+
*
|
|
542
|
+
* ```
|
|
543
|
+
* data = [
|
|
544
|
+
* {station: 'A', isFahreinheit: 'true', offset: '0', temp: 98, type: 'F', descr: '0123'},
|
|
545
|
+
* {station: 'A', isFahreinheit: 'TRUE', offset: '2', temp: 99, type: 'F', descr: '0123456'},
|
|
546
|
+
* {station: 'A', isFahreinheit: 'false', offset: '3', temp: 100, type: 'F', descr: '0123456789'}
|
|
547
|
+
* ];
|
|
548
|
+
*
|
|
549
|
+
* utils.object.format(data, ({
|
|
550
|
+
* //-- to a literal value
|
|
551
|
+
* type: 'C',
|
|
552
|
+
* //-- convert it to 'string', 'number' || 'float', 'int' || 'integer', 'boolean'
|
|
553
|
+
* offset: 'number',
|
|
554
|
+
* isFahreinheit: 'boolean',
|
|
555
|
+
* //-- or convert the value with a function accepting the current value
|
|
556
|
+
* //-- and returning the new value
|
|
557
|
+
* temp: (val) => (val - 32) * 0.5556
|
|
558
|
+
* }));
|
|
559
|
+
*
|
|
560
|
+
* // [
|
|
561
|
+
* // { station: 'A', isFahreinheit: true, offset: 0, temp: 36.669599999999996, type: 'C', descr: '0123' },
|
|
562
|
+
* // { station: 'A', isFahreinheit: true, offset: 2, temp: 37.2252, type: 'C', descr: '0123456' },
|
|
563
|
+
* // { station: 'A', isFahreinheit: false, offset: 3, temp: 37.7808, type: 'C', descr: '0123456789' }
|
|
564
|
+
* // ];
|
|
565
|
+
* ```
|
|
566
|
+
*
|
|
567
|
+
* **Please note, you can pass a single object to be cleaned**,<br /> but it will be returned as an array of one object.
|
|
568
|
+
*
|
|
569
|
+
* ```
|
|
570
|
+
* data = [{station: 'A', isFahreinheit: 'TRUE', offset: '2', temp: 99, type: 'F', descr: '0123456'}];
|
|
571
|
+
*
|
|
572
|
+
* utils.object.format(data, ({
|
|
573
|
+
* //-- convert it to 'string', 'number' || 'float', 'int' || 'integer', 'boolean'
|
|
574
|
+
* offset: 'number',
|
|
575
|
+
* isFahreinheit: 'boolean'
|
|
576
|
+
* }));
|
|
577
|
+
*
|
|
578
|
+
* // [{station: 'A', isFahreinheit: true, offset: 2, temp: 99, type: 'F', descr: '0123456'}];
|
|
579
|
+
* ```
|
|
580
|
+
*
|
|
581
|
+
* @param {Object} collection - the list of objects to update specific properties
|
|
582
|
+
* @param {Object} propertyTranslations - An object with property names as the properties to update <br />
|
|
583
|
+
* and the values as a function ((any) => any) accepting the current value, returning the new value.
|
|
584
|
+
* @returns {Object[]} - collection of objects transformed
|
|
585
|
+
* @see {@link module:object.augment|augment(collection, fn)} - to add in new properties
|
|
586
|
+
* @see {@link TableGenerator#formatter} - for other examples
|
|
587
|
+
*/
|
|
588
|
+
module.exports.formatProperties = function formatProperties(collection, propertyTranslations) {
|
|
589
|
+
const cleanCollection = !collection ? []
|
|
590
|
+
: Array.isArray(collection) ? collection : [collection];
|
|
591
|
+
|
|
592
|
+
propertyTranslations = FormatUtils.prepareFormatterObject(propertyTranslations);
|
|
593
|
+
const translationKeys = Array.from(Object.keys(propertyTranslations));
|
|
594
|
+
|
|
595
|
+
return cleanCollection.map((obj) => {
|
|
596
|
+
const clone = { ...obj };
|
|
597
|
+
translationKeys.forEach((key) => {
|
|
598
|
+
clone[key] = propertyTranslations[key](clone[key]);
|
|
599
|
+
});
|
|
600
|
+
return clone;
|
|
601
|
+
});
|
|
602
|
+
};
|
|
603
|
+
|
|
442
604
|
/**
|
|
443
605
|
* returns a map of the types of fields stored
|
|
444
606
|
* @see generateSchema
|