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 CHANGED
@@ -1,6 +1,8 @@
1
1
  # Overview
2
2
 
3
- This is a simple Library for using Jupyter with the IJavaScript kernel.
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.14-red" />
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.14.2",
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",
@@ -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(obj).forEach((key) => {
618
- if ((typeof obj[key]) === 'string') {
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
- //-- (based on fields of first record or columns provided)
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 = 50) {
311
- if (str && str.length > maxLen) {
312
- return `${str.substring(0, maxLen)}…`;
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 str;
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(objectKeys) {
330
+ module.exports.cleanPropertyNames = function cleanPropertyNames(target) {
247
331
  let originalKeys;
248
- if (Array.isArray(objectKeys)) {
249
- if ((typeof objectKeys[0]) === 'string') {
250
- originalKeys = objectKeys;
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(objectKeys);
338
+ originalKeys = ObjectUtils.keys(target);
253
339
  }
254
340
  } else {
255
- originalKeys = Object.keys(objectKeys);
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