jupyter-ijavascript-utils 1.9.1 → 1.10.1
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 +1 -0
- package/Dockerfile +34 -0
- package/README.md +2 -1
- package/package.json +2 -2
- package/src/TableGenerator.js +135 -10
- package/src/aggregate.js +197 -0
- package/src/file.js +86 -0
- package/src/format.js +41 -0
- package/src/object.js +189 -0
package/DOCS.md
CHANGED
|
@@ -28,6 +28,7 @@ See the [#Installation section for requirements and installation](#install)
|
|
|
28
28
|
|
|
29
29
|
## What's New
|
|
30
30
|
|
|
31
|
+
* 1.10 - provide {@link module:aggregate.percentile|percentile} (like 50th percentile) aggregates
|
|
31
32
|
* 1.9 - allow {@link TableGenerator#transpose|transposing results} on TableGenerator.
|
|
32
33
|
* 1.8 - add in What can I Do tutorial, and {@link module:object.join|object.join methods}
|
|
33
34
|
* 1.7 - revamp of `animation` method for ijs.htmlScript
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# per https://github.com/n-riesco/ijavascript/issues/273
|
|
2
|
+
# and https://github.com/paulroth3d/jupyter-ijavascript-utils/issues/4
|
|
3
|
+
|
|
4
|
+
# as of today this is python-3.7.1
|
|
5
|
+
FROM jupyter/base-notebook:latest
|
|
6
|
+
|
|
7
|
+
# for nbhosting
|
|
8
|
+
USER root
|
|
9
|
+
COPY start-in-dir-as-uid.sh /usr/local/bin
|
|
10
|
+
|
|
11
|
+
# prerequisites with apt-get
|
|
12
|
+
# we do install python(2) here because
|
|
13
|
+
# some npm build part named gyp still requires it
|
|
14
|
+
RUN apt-get update && apt-get install -y gcc g++ make python
|
|
15
|
+
|
|
16
|
+
# !!! dirty trick!!!
|
|
17
|
+
# original PATH is
|
|
18
|
+
# /opt/conda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
19
|
+
# move conda's path **at the end**
|
|
20
|
+
# so that python resolves in /usr/bin/python(2)
|
|
21
|
+
# but node is still found in conda
|
|
22
|
+
ENV PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/conda/bin"
|
|
23
|
+
USER jovyan
|
|
24
|
+
RUN npm install -g ijavascript
|
|
25
|
+
RUN ijsinstall
|
|
26
|
+
|
|
27
|
+
# for displaying html fragments
|
|
28
|
+
RUN npm install -g jsdom d3
|
|
29
|
+
|
|
30
|
+
# !!! clean up!!!
|
|
31
|
+
USER root
|
|
32
|
+
RUN apt-get autoremove -y python
|
|
33
|
+
ENV PATH="/opt/conda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
34
|
+
USER jovyan
|
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.10.0-red" />
|
|
11
11
|
</a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
@@ -21,6 +21,7 @@ See documentation at: [https://jupyter-ijavascript-utils.onrender.com/](https://
|
|
|
21
21
|
|
|
22
22
|
# What's New
|
|
23
23
|
|
|
24
|
+
* 1.10 - provide percentile (like 50th percentile) aggregates
|
|
24
25
|
* 1.9 - allow transposing results on TableGenerator.
|
|
25
26
|
* 1.8 - add in What can I Do tutorial, and object.join methods
|
|
26
27
|
* 1.7 - revamp of `animation` method to htmlScript
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyter-ijavascript-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.1",
|
|
4
4
|
"description": "Utilities for working with iJavaScript - a Jupyter Kernel",
|
|
5
5
|
"homepage": "https://jupyter-ijavascript-utils.onrender.com/",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"jupyter"
|
|
28
28
|
],
|
|
29
29
|
"author": "Paul Roth",
|
|
30
|
-
"license": "MIT",
|
|
31
30
|
"devDependencies": {
|
|
32
31
|
"babel-eslint": "^10.1.0",
|
|
33
32
|
"docdash": "^1.2.0",
|
|
@@ -44,6 +43,7 @@
|
|
|
44
43
|
"fs-extra": "^10.0.1",
|
|
45
44
|
"generate-schema": "^2.6.0",
|
|
46
45
|
"node-fetch": "^2.6.5",
|
|
46
|
+
"percentile": "^1.6.0",
|
|
47
47
|
"pino": "^6.12.0",
|
|
48
48
|
"pino-pretty": "^5.1.2",
|
|
49
49
|
"plantuml-encoder": "^1.4.0",
|
package/src/TableGenerator.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
const { printValue } = require('./format');
|
|
15
15
|
|
|
16
|
-
const generateRange = (length, defaultValue) => new Array(length).fill(defaultValue);
|
|
16
|
+
// const generateRange = (length, defaultValue) => new Array(length).fill(defaultValue);
|
|
17
17
|
|
|
18
18
|
const IJSUtils = require('./ijs');
|
|
19
19
|
|
|
@@ -82,6 +82,7 @@ const { createSort } = require('./array');
|
|
|
82
82
|
* * {@link TableGenerator#styleHeader|styleHeader(string)} - css styles for the header row
|
|
83
83
|
* * {@link TableGenerator#styleRow|styleRow(fn)} - Function to style rows
|
|
84
84
|
* * {@link TableGenerator#styleCell|styleCell(fn)} - Function to style cells
|
|
85
|
+
* * {@link TableGenerator#border|border(string)} - Apply a border to the table data cells
|
|
85
86
|
* * generate output
|
|
86
87
|
* * {@link TableGenerator#generateHTML|generateHTML()} - returns html table with the results
|
|
87
88
|
* * {@link TableGenerator#generateMarkdown|generateMarkdown()} - returns markdown with the results
|
|
@@ -105,6 +106,12 @@ class TableGenerator {
|
|
|
105
106
|
*/
|
|
106
107
|
#augmentFn = null;
|
|
107
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Border CSS to also apply to the cells
|
|
111
|
+
* @type {String}
|
|
112
|
+
*/
|
|
113
|
+
#borderCSS = ''; // 'solid 1px #AAA';
|
|
114
|
+
|
|
108
115
|
/**
|
|
109
116
|
* Optional array of exclusive columns to show based on the properties of each row\n
|
|
110
117
|
* ex: ['Miles_per_Gallon', 'Name', 'Cylinders', etc]
|
|
@@ -235,6 +242,7 @@ class TableGenerator {
|
|
|
235
242
|
reset() {
|
|
236
243
|
this.#data = [];
|
|
237
244
|
this.#augmentFn = null;
|
|
245
|
+
this.#borderCSS = '';
|
|
238
246
|
this.#columns = null;
|
|
239
247
|
this.#columnsToExclude = [];
|
|
240
248
|
this.#fetch = null;
|
|
@@ -338,6 +346,62 @@ class TableGenerator {
|
|
|
338
346
|
return this;
|
|
339
347
|
}
|
|
340
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Convenience function to set an a border on the Data Cells.
|
|
351
|
+
*
|
|
352
|
+
* This only applies when {@link TableGenerator#render|rendering HTML}
|
|
353
|
+
* or {@link TableGenerator#generateHTML|generating HTML}
|
|
354
|
+
*
|
|
355
|
+
* As this adds additional CSS, the styling applied:
|
|
356
|
+
* * {@link TableGenerator#styleTable|to the whole table}
|
|
357
|
+
* * or {@link TableGenerator#styleRow|to the rows}
|
|
358
|
+
* * or {@link TableGenerator#styleCell|to the data cells} will be affected
|
|
359
|
+
*
|
|
360
|
+
* For example:
|
|
361
|
+
*
|
|
362
|
+
* ```
|
|
363
|
+
* sourceData = [{id: 1, temp_F:98}, {id: 2, temp_F:99}, {id: 3, temp_F:100}];
|
|
364
|
+
*
|
|
365
|
+
* new utils.TableGenerator(sourceData)
|
|
366
|
+
* .border('1px solid #aaa')
|
|
367
|
+
* .render();
|
|
368
|
+
* ```
|
|
369
|
+
*
|
|
370
|
+
* <table cellspacing="0px" >
|
|
371
|
+
* <tr >
|
|
372
|
+
* <th>id</th>
|
|
373
|
+
* <th>temp_F</th>
|
|
374
|
+
* </tr>
|
|
375
|
+
* <tr >
|
|
376
|
+
* <td style=" border: 1px solid #aaa">1</td>
|
|
377
|
+
* <td style=" border: 1px solid #aaa">98</td>
|
|
378
|
+
* </tr>
|
|
379
|
+
* <tr >
|
|
380
|
+
* <td style=" border: 1px solid #aaa">2</td>
|
|
381
|
+
* <td style=" border: 1px solid #aaa">99</td>
|
|
382
|
+
* </tr>
|
|
383
|
+
* <tr >
|
|
384
|
+
* <td style=" border: 1px solid #aaa">3</td>
|
|
385
|
+
* <td style=" border: 1px solid #aaa">100</td>
|
|
386
|
+
* </tr>
|
|
387
|
+
* </table>
|
|
388
|
+
*
|
|
389
|
+
* @param {String | Boolean} borderCSS - CSS String to additionally apply HTML TD elements
|
|
390
|
+
*/
|
|
391
|
+
border(borderCSS) {
|
|
392
|
+
let cleanCSS = '';
|
|
393
|
+
|
|
394
|
+
if (borderCSS === true) {
|
|
395
|
+
cleanCSS = 'border: 1px solid #AAA';
|
|
396
|
+
} else if (borderCSS) {
|
|
397
|
+
cleanCSS = `border: ${borderCSS}`;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
this.#borderCSS = cleanCSS;
|
|
401
|
+
|
|
402
|
+
return this;
|
|
403
|
+
}
|
|
404
|
+
|
|
341
405
|
/**
|
|
342
406
|
* Applies an optional set of columns / properties to render
|
|
343
407
|
*
|
|
@@ -452,20 +516,64 @@ class TableGenerator {
|
|
|
452
516
|
*
|
|
453
517
|
* (This is an alternate to {@link formatterFn} or simple `.map()` call on the source data)
|
|
454
518
|
*
|
|
519
|
+
* **NOTE: Only matching properties on the formatter object are changed - all others are left alone.**
|
|
520
|
+
*
|
|
455
521
|
* For example:
|
|
456
522
|
*
|
|
457
523
|
* ```
|
|
458
|
-
* data = [
|
|
524
|
+
* data = [
|
|
525
|
+
* {station: 'A', temp: 98, type: 'F', descr: '0123'},
|
|
526
|
+
* {station: 'A', temp: 99, type: 'F', descr: '0123456'},
|
|
527
|
+
* {station: 'A', temp: 100, type: 'F', descr: '0123456789'}
|
|
528
|
+
* ];
|
|
459
529
|
*
|
|
460
530
|
* //-- simple example where the temp property is converted, and type property overwritten
|
|
461
531
|
* new TableGenerator(data)
|
|
462
532
|
* .formatter({
|
|
533
|
+
* //-- property 'station' not mentioned, so no change
|
|
534
|
+
*
|
|
535
|
+
* //-- convert temperature to celsius
|
|
463
536
|
* temp: (value) => (value - 32) * 0.5556,
|
|
464
|
-
* type
|
|
465
|
-
*
|
|
537
|
+
* //-- overwrite type from 'F' to 'C'
|
|
538
|
+
* type: 'C',
|
|
539
|
+
* //-- ellipsify to shorten the description string, if longer than 8 characters
|
|
540
|
+
* descr: (str) => utils.format.ellipsify(str, 8)
|
|
541
|
+
* }).renderMarkdown()
|
|
466
542
|
* ```
|
|
467
543
|
*
|
|
468
|
-
*
|
|
544
|
+
* station|temp |type|descr
|
|
545
|
+
* -- |-- |-- |--
|
|
546
|
+
* A |36.67 |F |0123
|
|
547
|
+
* A |37.225|F |0123456
|
|
548
|
+
* A |37.781|F |01234567…
|
|
549
|
+
*
|
|
550
|
+
* Note, due to frequent requests, simple datatype conversions can be requested.
|
|
551
|
+
*
|
|
552
|
+
* Only ('String', 'Number', and 'Boolean') are supported
|
|
553
|
+
*
|
|
554
|
+
* ```
|
|
555
|
+
* data = [
|
|
556
|
+
* { propA: ' 8009', propB: 8009, isBoolean: 0},
|
|
557
|
+
* { propA: ' 92032', propB: 92032, isBoolean: 1},
|
|
558
|
+
* { propA: ' 234234', propB: 234234, isBoolean: 1},
|
|
559
|
+
* ];
|
|
560
|
+
*
|
|
561
|
+
* new utils.TableGenerator(data)
|
|
562
|
+
* .formatter({
|
|
563
|
+
* //-- convert Prop A to Number - so render with Locale Number Formatting
|
|
564
|
+
* propA: 'number',
|
|
565
|
+
* //-- conver PropB to String - so render without Locale Number Formatting
|
|
566
|
+
* propB: 'string',
|
|
567
|
+
* //-- render 'True' or 'False'
|
|
568
|
+
* isBoolean: 'boolean'
|
|
569
|
+
* }).renderMarkdown();
|
|
570
|
+
* ```
|
|
571
|
+
*
|
|
572
|
+
* propA|propB|isBoolean
|
|
573
|
+
* -- |-- |--
|
|
574
|
+
* 8,009 |8009 |false
|
|
575
|
+
* 92,032 |92032 |true
|
|
576
|
+
* 234,234 |234234 |true
|
|
469
577
|
*
|
|
470
578
|
* @param {Object} obj - object with properties storing arrow functions
|
|
471
579
|
* @param {Function} obj.PropertyToTranslate - (value) => result
|
|
@@ -480,10 +588,25 @@ class TableGenerator {
|
|
|
480
588
|
|
|
481
589
|
const fnMap = new Map();
|
|
482
590
|
Object.getOwnPropertyNames(obj).forEach((key) => {
|
|
483
|
-
if ((typeof obj[key])
|
|
484
|
-
|
|
591
|
+
if ((typeof obj[key]) === 'string') {
|
|
592
|
+
let fn;
|
|
593
|
+
const str = obj[key].toLowerCase();
|
|
594
|
+
if (str === 'string') {
|
|
595
|
+
fn = (val) => String(val);
|
|
596
|
+
} else if (str === 'number') {
|
|
597
|
+
fn = (val) => Number(val);
|
|
598
|
+
} else if (str === 'boolean') {
|
|
599
|
+
fn = (val) => val ? 'true' : 'false';
|
|
600
|
+
} else {
|
|
601
|
+
throw Error(`TableGenerator.format: property ${key} formatter of ${str} is unsupported. Only (String, Number, Boolean) are supported`);
|
|
602
|
+
}
|
|
603
|
+
fnMap.set(key, fn);
|
|
604
|
+
} else {
|
|
605
|
+
if ((typeof obj[key]) !== 'function') {
|
|
606
|
+
throw (Error(`Formatter properties must be functions. [${key}]`));
|
|
607
|
+
}
|
|
608
|
+
fnMap.set(key, obj[key]);
|
|
485
609
|
}
|
|
486
|
-
fnMap.set(key, obj[key]);
|
|
487
610
|
});
|
|
488
611
|
|
|
489
612
|
this.#formatterFn = ({ value, property }) => fnMap.has(property)
|
|
@@ -928,6 +1051,7 @@ class TableGenerator {
|
|
|
928
1051
|
const styleRowFn = this.#styleRow;
|
|
929
1052
|
const styleCellFn = this.#styleCell;
|
|
930
1053
|
const printOptions = this.#printOptions;
|
|
1054
|
+
const borderCSS = this.#borderCSS;
|
|
931
1055
|
|
|
932
1056
|
const cleanFn = printValue;
|
|
933
1057
|
|
|
@@ -948,16 +1072,17 @@ class TableGenerator {
|
|
|
948
1072
|
+ dataRow.map((value, columnIndex) => {
|
|
949
1073
|
//-- note - the data is from the original dataset, not the results
|
|
950
1074
|
const cellStyle = !styleCellFn
|
|
951
|
-
?
|
|
1075
|
+
? ''
|
|
952
1076
|
: styleCellFn({ value, columnIndex, rowIndex, row: dataRow, record });
|
|
953
1077
|
return '<td '
|
|
954
|
-
+ (
|
|
1078
|
+
+ (borderCSS || cellStyle ? `style="${cellStyle} ${borderCSS}"` : '')
|
|
955
1079
|
+ `>${cleanFn(value, printOptions)}</td>`;
|
|
956
1080
|
}).join('\n\t')
|
|
957
1081
|
+ '\n</tr>';
|
|
958
1082
|
}).join('\n');
|
|
959
1083
|
|
|
960
1084
|
const tableResults = '<table '
|
|
1085
|
+
+ 'cellspacing="0px" '
|
|
961
1086
|
+ (!styleTable ? '' : `style="${styleTable}"`)
|
|
962
1087
|
+ '>'
|
|
963
1088
|
+ printHeader(results.headers, '')
|
package/src/aggregate.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* eslint-disable implicit-arrow-linebreak */
|
|
2
2
|
|
|
3
|
+
const Percentile = require('percentile');
|
|
4
|
+
|
|
3
5
|
const ObjectUtils = require('./object');
|
|
4
6
|
const FormatUtils = require('./format');
|
|
5
7
|
|
|
@@ -13,6 +15,8 @@ const FormatUtils = require('./format');
|
|
|
13
15
|
*
|
|
14
16
|
* Types of methods:
|
|
15
17
|
*
|
|
18
|
+
* * Select a single property
|
|
19
|
+
* * {@link module:aggregate.property|property()} - maps to a single property (often used with other libraries)
|
|
16
20
|
* * Ranges of values
|
|
17
21
|
* * {@link module:aggregate.extent|extent()} - returns the min and max of range
|
|
18
22
|
* * {@link module:aggregate.min|min()} - returns the minimum value of the range
|
|
@@ -35,6 +39,17 @@ const FormatUtils = require('./format');
|
|
|
35
39
|
* * {@link module:aggregate.sum|sum()} - sum of a collection
|
|
36
40
|
* * Functional
|
|
37
41
|
* * {@link module:aggregate.deferCollection|deferCollection(function, bindArg, bindArg, ...)} - bind a function with arguments
|
|
42
|
+
* * Percentile
|
|
43
|
+
* * {@link module:aggregate.percentile|percentile()} - determines the Nth percentile of a field or value
|
|
44
|
+
* * {@link module:aggregate.percentile_01|percentile_01()} - 1th percentile
|
|
45
|
+
* * {@link module:aggregate.percentile_05|percentile_05()} - 5th percentile
|
|
46
|
+
* * {@link module:aggregate.percentile_10|percentile_10()} - 10th percentile
|
|
47
|
+
* * {@link module:aggregate.percentile_25|percentile_25()} - 25th percentile
|
|
48
|
+
* * {@link module:aggregate.percentile_50|percentile_50()} - 50th percentile
|
|
49
|
+
* * {@link module:aggregate.percentile_75|percentile_75()} - 75th percentile
|
|
50
|
+
* * {@link module:aggregate.percentile_90|percentile_90()} - 90th percentile
|
|
51
|
+
* * {@link module:aggregate.percentile_95|percentile_95()} - 95th percentile
|
|
52
|
+
* * {@link module:aggregate.percentile_99|percentile_99()} - 99th percentile
|
|
38
53
|
*
|
|
39
54
|
* Please note, there is nothing special for these functions, such as working with {@link SourceMap#reduce|SourceMap.reduce()}
|
|
40
55
|
*
|
|
@@ -225,6 +240,34 @@ module.exports = {};
|
|
|
225
240
|
// eslint-disable-next-line no-unused-vars
|
|
226
241
|
const AggregateUtils = module.exports;
|
|
227
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Maps an array of values to a single property.
|
|
245
|
+
*
|
|
246
|
+
* For example:
|
|
247
|
+
*
|
|
248
|
+
* ```
|
|
249
|
+
* const data = [{ record: 'jobA', val: 1 }, { record: 'jobA', val: 2 },
|
|
250
|
+
* { record: 'jobA', val: 3 }, { record: 'jobA', val: 4 },
|
|
251
|
+
* { record: 'jobA', val: 5 }, { record: 'jobA', val: 6 },
|
|
252
|
+
* { record: 'jobA', val: 7 }, { record: 'jobA', val: 8 },
|
|
253
|
+
* { record: 'jobA', val: 9 }, { record: 'jobA', val: 10 }
|
|
254
|
+
* ];
|
|
255
|
+
*
|
|
256
|
+
* utils.object.propertyFromList(data, 'val')
|
|
257
|
+
* //-- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
258
|
+
*
|
|
259
|
+
* utils.object.propertyFromList(data, (r) => r.val);
|
|
260
|
+
* //-- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
261
|
+
* ```
|
|
262
|
+
*
|
|
263
|
+
* @param {Object[]} objectArray - Array of Objects to be mapped to a single property / value
|
|
264
|
+
* @param {Function | String} propertyOrFn - Name of the property or Function to return a value
|
|
265
|
+
* @returns {Array} - Array of values
|
|
266
|
+
*/
|
|
267
|
+
module.exports.property = function propertyFromList(objectArray, propertyOrFn) {
|
|
268
|
+
return ObjectUtils.propertyFromList(objectArray, propertyOrFn);
|
|
269
|
+
};
|
|
270
|
+
|
|
228
271
|
/**
|
|
229
272
|
* Converts an aggregate function to two functions -
|
|
230
273
|
* one that takes all arguments except the collection
|
|
@@ -662,3 +705,157 @@ module.exports.isUnique = function isUnique(collection, accessor) {
|
|
|
662
705
|
});
|
|
663
706
|
return duplicateValue === undefined;
|
|
664
707
|
};
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Returns a given percentile from a list of objects.
|
|
711
|
+
*
|
|
712
|
+
* **Note: this simply aggregates the values and passes to the [Percentile NPM Package](https://www.npmjs.com/package/percentile)**
|
|
713
|
+
*
|
|
714
|
+
* @param {Object[]} collection - collection of objects
|
|
715
|
+
* @param {Function | String} accessor - function to access the value, string property or null
|
|
716
|
+
* @param {Number} pct - Percentile (either .5 or 50)
|
|
717
|
+
* @returns {Number} - the pct percentile of a property within the collection
|
|
718
|
+
* @example
|
|
719
|
+
* const data = [{ record: 'jobA', val: 1 }, { record: 'jobA', val: 2 },
|
|
720
|
+
* { record: 'jobA', val: 3 }, { record: 'jobA', val: 4 },
|
|
721
|
+
* { record: 'jobA', val: 5 }, { record: 'jobA', val: 6 },
|
|
722
|
+
* { record: 'jobA', val: 7 }, { record: 'jobA', val: 8 },
|
|
723
|
+
* { record: 'jobA', val: 9 }, { record: 'jobA', val: 10 }
|
|
724
|
+
* ];
|
|
725
|
+
*
|
|
726
|
+
* utils.aggregate.percentile(data, 'val', 50) //-- returns 5
|
|
727
|
+
* utils.aggregate.percentile(data, (r) => r.val, 70) //-- returns 7
|
|
728
|
+
*/
|
|
729
|
+
module.exports.percentile = function percentile(collection, accessor, pct) {
|
|
730
|
+
const values = ObjectUtils.propertyFromList(collection, accessor);
|
|
731
|
+
const cleanPercentile = pct > 0 && pct < 1
|
|
732
|
+
? pct * 100
|
|
733
|
+
: pct;
|
|
734
|
+
return Percentile(cleanPercentile, values);
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Returns a hard coded percentage
|
|
739
|
+
*
|
|
740
|
+
* {@link module:aggregate.percentage|See Percentage for more detail}
|
|
741
|
+
*
|
|
742
|
+
* @param {Object[]} collection - collection of objects
|
|
743
|
+
* @param {Function | String} accessor - function to access the value, string property or null
|
|
744
|
+
* @returns {Number} - the percentile of a property within the collection
|
|
745
|
+
* @see {@link module:aggregate.percentile|percentile} - as this simply hard codes the percentage
|
|
746
|
+
*/
|
|
747
|
+
module.exports.percentile_01 = function percentile(collection, accessor) {
|
|
748
|
+
return AggregateUtils.percentile(collection, accessor, 1);
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Returns a hard coded percentage
|
|
753
|
+
*
|
|
754
|
+
* {@link module:aggregate.percentage|See Percentage for more detail}
|
|
755
|
+
*
|
|
756
|
+
* @param {Object[]} collection - collection of objects
|
|
757
|
+
* @param {Function | String} accessor - function to access the value, string property or null
|
|
758
|
+
* @returns {Number} - the percentile of a property within the collection
|
|
759
|
+
* @see {@link module:aggregate.percentile|percentile} - as this simply hard codes the percentage
|
|
760
|
+
*/
|
|
761
|
+
module.exports.percentile_05 = function percentile(collection, accessor) {
|
|
762
|
+
return AggregateUtils.percentile(collection, accessor, 5);
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Returns a hard coded percentage
|
|
767
|
+
*
|
|
768
|
+
* {@link module:aggregate.percentage|See Percentage for more detail}
|
|
769
|
+
*
|
|
770
|
+
* @param {Object[]} collection - collection of objects
|
|
771
|
+
* @param {Function | String} accessor - function to access the value, string property or null
|
|
772
|
+
* @returns {Number} - the percentile of a property within the collection
|
|
773
|
+
* @see {@link module:aggregate.percentile|percentile} - as this simply hard codes the percentage
|
|
774
|
+
*/
|
|
775
|
+
module.exports.percentile_10 = function percentile(collection, accessor) {
|
|
776
|
+
return AggregateUtils.percentile(collection, accessor, 10);
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Returns a hard coded percentage
|
|
781
|
+
*
|
|
782
|
+
* {@link module:aggregate.percentage|See Percentage for more detail}
|
|
783
|
+
*
|
|
784
|
+
* @param {Object[]} collection - collection of objects
|
|
785
|
+
* @param {Function | String} accessor - function to access the value, string property or null
|
|
786
|
+
* @returns {Number} - the percentile of a property within the collection
|
|
787
|
+
* @see {@link module:aggregate.percentile|percentile} - as this simply hard codes the percentage
|
|
788
|
+
*/
|
|
789
|
+
module.exports.percentile_25 = function percentile(collection, accessor) {
|
|
790
|
+
return AggregateUtils.percentile(collection, accessor, 25);
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Returns a hard coded percentage
|
|
795
|
+
*
|
|
796
|
+
* {@link module:aggregate.percentage|See Percentage for more detail}
|
|
797
|
+
*
|
|
798
|
+
* @param {Object[]} collection - collection of objects
|
|
799
|
+
* @param {Function | String} accessor - function to access the value, string property or null
|
|
800
|
+
* @returns {Number} - the percentile of a property within the collection
|
|
801
|
+
* @see {@link module:aggregate.percentile|percentile} - as this simply hard codes the percentage
|
|
802
|
+
*/
|
|
803
|
+
module.exports.percentile_50 = function percentile(collection, accessor) {
|
|
804
|
+
return AggregateUtils.percentile(collection, accessor, 50);
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Returns a hard coded percentage
|
|
809
|
+
*
|
|
810
|
+
* {@link module:aggregate.percentage|See Percentage for more detail}
|
|
811
|
+
*
|
|
812
|
+
* @param {Object[]} collection - collection of objects
|
|
813
|
+
* @param {Function | String} accessor - function to access the value, string property or null
|
|
814
|
+
* @returns {Number} - the percentile of a property within the collection
|
|
815
|
+
* @see {@link module:aggregate.percentile|percentile} - as this simply hard codes the percentage
|
|
816
|
+
*/
|
|
817
|
+
module.exports.percentile_75 = function percentile(collection, accessor) {
|
|
818
|
+
return AggregateUtils.percentile(collection, accessor, 75);
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Returns a hard coded percentage
|
|
823
|
+
*
|
|
824
|
+
* {@link module:aggregate.percentage|See Percentage for more detail}
|
|
825
|
+
*
|
|
826
|
+
* @param {Object[]} collection - collection of objects
|
|
827
|
+
* @param {Function | String} accessor - function to access the value, string property or null
|
|
828
|
+
* @returns {Number} - the percentile of a property within the collection
|
|
829
|
+
* @see {@link module:aggregate.percentile|percentile} - as this simply hard codes the percentage
|
|
830
|
+
*/
|
|
831
|
+
module.exports.percentile_90 = function percentile(collection, accessor) {
|
|
832
|
+
return AggregateUtils.percentile(collection, accessor, 90);
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Returns a hard coded percentage
|
|
837
|
+
*
|
|
838
|
+
* {@link module:aggregate.percentage|See Percentage for more detail}
|
|
839
|
+
*
|
|
840
|
+
* @param {Object[]} collection - collection of objects
|
|
841
|
+
* @param {Function | String} accessor - function to access the value, string property or null
|
|
842
|
+
* @returns {Number} - the percentile of a property within the collection
|
|
843
|
+
* @see {@link module:aggregate.percentile|percentile} - as this simply hard codes the percentage
|
|
844
|
+
*/
|
|
845
|
+
module.exports.percentile_95 = function percentile(collection, accessor) {
|
|
846
|
+
return AggregateUtils.percentile(collection, accessor, 95);
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Returns a hard coded percentage
|
|
851
|
+
*
|
|
852
|
+
* {@link module:aggregate.percentage|See Percentage for more detail}
|
|
853
|
+
*
|
|
854
|
+
* @param {Object[]} collection - collection of objects
|
|
855
|
+
* @param {Function | String} accessor - function to access the value, string property or null
|
|
856
|
+
* @returns {Number} - the percentile of a property within the collection
|
|
857
|
+
* @see {@link module:aggregate.percentile|percentile} - as this simply hard codes the percentage
|
|
858
|
+
*/
|
|
859
|
+
module.exports.percentile_99 = function percentile(collection, accessor) {
|
|
860
|
+
return AggregateUtils.percentile(collection, accessor, 99);
|
|
861
|
+
};
|
package/src/file.js
CHANGED
|
@@ -26,6 +26,8 @@ const logger = require('./logger');
|
|
|
26
26
|
* * listing directory
|
|
27
27
|
* * {@link module:file.pwd|pwd()} - list the current path
|
|
28
28
|
* * {@link module:file.listFiles|listFiles(path)} - list files in a diven path
|
|
29
|
+
* * checking files exist
|
|
30
|
+
* * {@link module:file.checkFile|checkFile(...paths)} - check if a file at a path exists
|
|
29
31
|
*
|
|
30
32
|
* ---
|
|
31
33
|
*
|
|
@@ -299,3 +301,87 @@ module.exports.listFiles = function listFiles(directoryPath) {
|
|
|
299
301
|
logger.error(`unable to read directory: ${resolvedPath}`);
|
|
300
302
|
}
|
|
301
303
|
};
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Synchronously checks if any of the files provided do not exist.
|
|
307
|
+
*
|
|
308
|
+
* For example:
|
|
309
|
+
*
|
|
310
|
+
* ```
|
|
311
|
+
* //-- these exist
|
|
312
|
+
* // ./data/credentials.env
|
|
313
|
+
* // ./data/results.json
|
|
314
|
+
*
|
|
315
|
+
* if (!utils.file.checkFile('./data/results.json')) {
|
|
316
|
+
* //-- retrieve the results
|
|
317
|
+
* utils.ijs.await(async($$, console) => {
|
|
318
|
+
* results = await connection.query('SELECT XYZ from Contacts');
|
|
319
|
+
* utils.file.write('./data/results.json', results);
|
|
320
|
+
* });
|
|
321
|
+
* } else {
|
|
322
|
+
* results = utils.file.readJSON('./data/results.json');
|
|
323
|
+
* }
|
|
324
|
+
* ```
|
|
325
|
+
*
|
|
326
|
+
* Note, you can also ask for multiple files at once
|
|
327
|
+
*
|
|
328
|
+
* ```
|
|
329
|
+
* utils.file.checkFile(
|
|
330
|
+
* './data/credentials.env',
|
|
331
|
+
* './data/results.json',
|
|
332
|
+
* './data/results.csv'
|
|
333
|
+
* );
|
|
334
|
+
* // false
|
|
335
|
+
* ```
|
|
336
|
+
*
|
|
337
|
+
* or as an array:
|
|
338
|
+
*
|
|
339
|
+
* ```
|
|
340
|
+
* utils.file.checkFile(['./data/credentails.env']);
|
|
341
|
+
* // true
|
|
342
|
+
* ```
|
|
343
|
+
*
|
|
344
|
+
* @param {...String} files - List of file paths to check (can use relative paths, like './') <br />
|
|
345
|
+
* see {@link file:listFiles|listFiles()} or {@link file:pwd|pwd()} to help you)
|
|
346
|
+
* @returns {String[]} - null if all files are found, or array of string paths of files not found
|
|
347
|
+
*/
|
|
348
|
+
module.exports.checkFile = function checkFile(...files) {
|
|
349
|
+
//-- allow passing an array of files
|
|
350
|
+
const cleanFiles = files.length === 1 && Array.isArray(files[0])
|
|
351
|
+
? files[0]
|
|
352
|
+
: files;
|
|
353
|
+
|
|
354
|
+
const resolvedFiles = cleanFiles.map((unresolvedPath) => path.resolve(unresolvedPath));
|
|
355
|
+
|
|
356
|
+
const notFoundFiles = resolvedFiles.map((resolvedPath) => fs.existsSync(resolvedPath)
|
|
357
|
+
? null
|
|
358
|
+
: resolvedPath);
|
|
359
|
+
|
|
360
|
+
//-- do not filter empty files, as position in array is helpful
|
|
361
|
+
if (notFoundFiles.filter((p) => p).length === 0) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return notFoundFiles;
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
/*
|
|
369
|
+
* Execute an async function if any of the files do not exist
|
|
370
|
+
* @param {String[]} filePaths - list of paths of files to check that they exist
|
|
371
|
+
* @param {*} fnIfFailed - async function tha will run - but only if any of the files are not found.
|
|
372
|
+
*/
|
|
373
|
+
/*
|
|
374
|
+
module.exports.ifNotExists = async function ifNotExists(filePaths, fnIfFailed) {
|
|
375
|
+
const filesNotFound = FileUtil.checkFile(filePaths);
|
|
376
|
+
|
|
377
|
+
let results;
|
|
378
|
+
|
|
379
|
+
if (filesNotFound) {
|
|
380
|
+
results = await fnIfFailed(filesNotFound);
|
|
381
|
+
} else {
|
|
382
|
+
results = null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return results;
|
|
386
|
+
};
|
|
387
|
+
*/
|
package/src/format.js
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* * formatting Numbers
|
|
10
10
|
* * {@link module:format.zeroFill|format.zeroFill} - Pads a number to a specific length
|
|
11
11
|
* * {@link module:format.divideR|format.divideR} - Divides a number to provide { integer, remainder } - ex: 5/3 as ( 1, remainder 2 )
|
|
12
|
+
* * Formatting Strings
|
|
13
|
+
* * {@link module:format.capitalize|format.capitalize} - Capitalizes only the first character in the string (ex: 'John paul');
|
|
14
|
+
* * {@link module:format.capitalizeAll|format.capitalizeAll} - Capitalizes all the words in a string (ex: 'John Paul')
|
|
15
|
+
* * {@link module:format.ellipsify|format.ellipsify} - Truncates a string if the length is 'too long'
|
|
12
16
|
* * Formatting Time
|
|
13
17
|
* * {@link module:format.millisecondDuration|format.millisecondDuration}
|
|
14
18
|
* * Mapping Values
|
|
@@ -454,3 +458,40 @@ module.exports.clampDomain = function clampDomain(value, [minimum, maximum]) {
|
|
|
454
458
|
}
|
|
455
459
|
return value;
|
|
456
460
|
};
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Capitalizes the first character of the string.
|
|
464
|
+
*
|
|
465
|
+
* @param {String} str - String to capitalize the first letter only
|
|
466
|
+
* @returns {String} - ex: 'John paul'
|
|
467
|
+
* @see {@link module:format.capitalizeAll|capitalizeAll} - to capitalize all words in a string
|
|
468
|
+
* @example
|
|
469
|
+
* utils.format.capitalize('john'); // 'John'
|
|
470
|
+
* utils.format.capitalize('john doe'); // 'John doe'
|
|
471
|
+
*/
|
|
472
|
+
module.exports.capitalize = function capitalize(str) {
|
|
473
|
+
if (!str || str.length === 0) {
|
|
474
|
+
return '';
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
//-- charAt does not work for unicode
|
|
478
|
+
const [first, ...rest] = str;
|
|
479
|
+
return first.toLocaleUpperCase() + rest.join('');
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Capitalizes all words in a string.
|
|
484
|
+
*
|
|
485
|
+
* @param {String} str - String to capitalize
|
|
486
|
+
* @returns {String} - ex: 'John-Paul'
|
|
487
|
+
* @see {@link module:format.capitalizeAll|capitalizeAll} - to capitalize all words in a string
|
|
488
|
+
* @example
|
|
489
|
+
* utils.format.capitalize('john'); // 'John'
|
|
490
|
+
* utils.format.capitalize('john doe'); // 'John Doe'
|
|
491
|
+
* utils.format.capitalize('john-paul'); // 'John-Paul'
|
|
492
|
+
*/
|
|
493
|
+
module.exports.capitalizeAll = function capitalizeAll(str) {
|
|
494
|
+
return (str || '').split(/\b/)
|
|
495
|
+
.map(FormatUtils.capitalize)
|
|
496
|
+
.join('');
|
|
497
|
+
};
|
package/src/object.js
CHANGED
|
@@ -9,6 +9,9 @@ const schemaGenerator = require('generate-schema');
|
|
|
9
9
|
* * {@link module:object.keys|keys()} - Safely get the keys of an object or list of objects
|
|
10
10
|
* * {@link module:object.getObjectPropertyTypes|getObjectPropertyTypes()} - describe the properties of a list of objects
|
|
11
11
|
* * {@link module:object.generateSchema|generateSchema()} - generate a schema / describe properties of a list of objects
|
|
12
|
+
* * {@link module:object.findWithoutProperties|findWithoutProperties()} - find objects without ALL the properties specified
|
|
13
|
+
* * {@link module:object.findWithoutProperties|findWithProperties()} - find objects with any of the properties specified
|
|
14
|
+
* * {@link module:object.setPropertyDefaults|setPropertyDefaults()} - sets values for objects that don't currently have the property
|
|
12
15
|
* * Manipulating objects
|
|
13
16
|
* * {@link module:object.objAssign|objAssign()} -
|
|
14
17
|
* * {@link module:object.objAssignEntities|objAssignEntities()} -
|
|
@@ -19,6 +22,7 @@ const schemaGenerator = require('generate-schema');
|
|
|
19
22
|
* * {@link module:object.fetchObjectProperties|fetchObjectProperties(object, string[])} - use dot notation to bring multiple child properties onto a parent
|
|
20
23
|
* * {@link module:object.join|join(array, index, map, fn)} - join a collection against a map by a given index
|
|
21
24
|
* * {@link module:object.joinProperties|join(array, index, map, ...fields)} - join a collection, and copy properties over from the mapped object.
|
|
25
|
+
* * {@link module:object.propertyFromList|propertyFromList(array, propertyName)} - fetches a specific property from all objects in a list
|
|
22
26
|
* * Rename properties
|
|
23
27
|
* * {@link module:object.cleanProperties|cleanProperties()} - correct inaccessible property names in a list of objects
|
|
24
28
|
* * {@link module:object.cleanPropertyNames|cleanPropertyNames()} - create a translation of inaccessible names to accessible ones
|
|
@@ -634,3 +638,188 @@ module.exports.joinProperties = function join(objectArray, indexField, targetMap
|
|
|
634
638
|
|
|
635
639
|
return ObjectUtils.join(objectArray, indexField, targetMap, joinFn);
|
|
636
640
|
};
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Maps an array of values to a single property.
|
|
644
|
+
*
|
|
645
|
+
* For example:
|
|
646
|
+
*
|
|
647
|
+
* ```
|
|
648
|
+
* const data = [{ record: 'jobA', val: 1 }, { record: 'jobA', val: 2 },
|
|
649
|
+
* { record: 'jobA', val: 3 }, { record: 'jobA', val: 4 },
|
|
650
|
+
* { record: 'jobA', val: 5 }, { record: 'jobA', val: 6 },
|
|
651
|
+
* { record: 'jobA', val: 7 }, { record: 'jobA', val: 8 },
|
|
652
|
+
* { record: 'jobA', val: 9 }, { record: 'jobA', val: 10 }
|
|
653
|
+
* ];
|
|
654
|
+
*
|
|
655
|
+
* utils.object.propertyFromList(data, 'val')
|
|
656
|
+
* //-- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
657
|
+
*
|
|
658
|
+
* utils.object.propertyFromList(data, (r) => r.val);
|
|
659
|
+
* //-- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
660
|
+
* ```
|
|
661
|
+
*
|
|
662
|
+
* @param {Object[]} objectArray - Array of Objects to be mapped to a single property / value
|
|
663
|
+
* @param {Function | String} propertyOrFn - Name of the property or Function to return a value
|
|
664
|
+
* @returns {Array} - Array of values
|
|
665
|
+
*/
|
|
666
|
+
module.exports.propertyFromList = function propertyFromList(objectArray, propertyOrFn) {
|
|
667
|
+
const cleanArray = Array.isArray(objectArray)
|
|
668
|
+
? objectArray
|
|
669
|
+
: [];
|
|
670
|
+
|
|
671
|
+
const fn = ObjectUtils.evaluateFunctionOrProperty(propertyOrFn);
|
|
672
|
+
|
|
673
|
+
return cleanArray.map(fn);
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Finds objects that do not have ALL the properties specified.
|
|
678
|
+
*
|
|
679
|
+
* This can be very helpful in ensuring all objects actually meet a specification and are not missing values.
|
|
680
|
+
*
|
|
681
|
+
* ```
|
|
682
|
+
* const students = [
|
|
683
|
+
* { first: 'john', last: 'doe', age: 23 }, { first: 'jane', last: 'doe', age: 23 }, { first: 'jack', last: 'white', failure: 401 }
|
|
684
|
+
* ];
|
|
685
|
+
*
|
|
686
|
+
* utils.findWithoutProperties(students, 'first', 'last', 'age');
|
|
687
|
+
* // [{ first: 'jack', last: 'white', failure: 401 }]
|
|
688
|
+
*
|
|
689
|
+
* utils.findWithoutProperties(students, 'failure');
|
|
690
|
+
* // [{ first: 'john', last: 'doe', age: 23 }, { first: 'jane', last: 'doe', age: 23 }]
|
|
691
|
+
* ```
|
|
692
|
+
*
|
|
693
|
+
* Please note, that we can check a single object:
|
|
694
|
+
*
|
|
695
|
+
* ```
|
|
696
|
+
* utils.findWithoutProperties(students[0], 'failure');
|
|
697
|
+
* // []
|
|
698
|
+
* ```
|
|
699
|
+
*
|
|
700
|
+
* @param {Object[]} objectsToCheck - the array of objects to check for the properties.
|
|
701
|
+
* @param {...String} propertiesToFind - the list of properties to find within the collection.
|
|
702
|
+
* @returns {Object[]} - Array of objects that are missing at least one of those properties
|
|
703
|
+
* @see {@link module:file.findWithProperties|findWithProperties} - if you want objects that do not have all properties
|
|
704
|
+
**/
|
|
705
|
+
module.exports.findWithoutProperties = function findWithoutProperties(targetObj, ...propertiesToFind) {
|
|
706
|
+
const cleanProperties = propertiesToFind.length > 0 && Array.isArray(propertiesToFind[0])
|
|
707
|
+
? propertiesToFind[0]
|
|
708
|
+
: propertiesToFind;
|
|
709
|
+
|
|
710
|
+
const cleanTargets = Array.isArray(targetObj)
|
|
711
|
+
? targetObj
|
|
712
|
+
: [targetObj];
|
|
713
|
+
|
|
714
|
+
const results = [];
|
|
715
|
+
|
|
716
|
+
cleanTargets.forEach((target) => {
|
|
717
|
+
if (cleanProperties.find((prop) => (typeof target[prop]) === 'undefined')) {
|
|
718
|
+
results.push(target);
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
return results;
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Finds objects that have any of the properties specified.
|
|
727
|
+
*
|
|
728
|
+
* This can be very helpful when working with datasets that include mixed data (such as JSON)
|
|
729
|
+
*
|
|
730
|
+
* ```
|
|
731
|
+
* const students = [
|
|
732
|
+
* { first: 'john', last: 'doe' }, { first: 'jane', last: 'doe' }, { first: 'jack', last: 'white', failure: 401 }
|
|
733
|
+
* ];
|
|
734
|
+
*
|
|
735
|
+
* utils.findWithProperties(students, 'failure');
|
|
736
|
+
* // { first: 'jack', last: 'white', failure: 401 }
|
|
737
|
+
* ```
|
|
738
|
+
*
|
|
739
|
+
* Please note, that we can check a single object:
|
|
740
|
+
*
|
|
741
|
+
* ```
|
|
742
|
+
* utils.findWithProperties({ first: 'john', last: 'doe' }, 'failure');
|
|
743
|
+
* // []
|
|
744
|
+
* ```
|
|
745
|
+
*
|
|
746
|
+
* @param {Object[]} objectsToCheck - the array of objects to check for the properties.
|
|
747
|
+
* @param {...String} propertiesToFind - the list of properties to find within the collection.
|
|
748
|
+
* @returns {Object[]} - Array of objects that have at least one of those properties
|
|
749
|
+
* @see {@link module:file.findWithoutProperties|findWithoutProperties} - if you want objects that do not have all properties
|
|
750
|
+
**/
|
|
751
|
+
module.exports.findWithProperties = function findWithProperties(targetObj, ...propertiesToFind) {
|
|
752
|
+
const cleanProperties = propertiesToFind.length > 0 && Array.isArray(propertiesToFind[0])
|
|
753
|
+
? propertiesToFind[0]
|
|
754
|
+
: propertiesToFind;
|
|
755
|
+
|
|
756
|
+
const cleanTargets = Array.isArray(targetObj)
|
|
757
|
+
? targetObj
|
|
758
|
+
: [targetObj];
|
|
759
|
+
|
|
760
|
+
const results = [];
|
|
761
|
+
|
|
762
|
+
cleanTargets.forEach((target) => {
|
|
763
|
+
if (cleanProperties.find((prop) => (typeof target[prop]) !== 'undefined')) {
|
|
764
|
+
results.push(target);
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
return results;
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Sets values for objects that don't currently have the property
|
|
773
|
+
*
|
|
774
|
+
* This is very helpful for ensuring that all objects have a property,
|
|
775
|
+
* or setting a value to make it easier to identify that it is 'N/A'
|
|
776
|
+
*
|
|
777
|
+
* Note, that only the {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty|ownProperties}
|
|
778
|
+
* on the default object are checked.
|
|
779
|
+
*
|
|
780
|
+
* And values are applied to the target object, only if the property is not on the object (property is undefined)
|
|
781
|
+
*
|
|
782
|
+
* @param {Object[] | Object} targetObject - Object to apply the properties to <br />
|
|
783
|
+
* but ONLY if the object does not have that property (ex: undefined)
|
|
784
|
+
* @param {Object} defaultObj - Object with the properties and defaults applied
|
|
785
|
+
* @param {any} defaultObj.property - the property to check, with the default value assigned
|
|
786
|
+
* @see {@link module:file.findWithoutProperties|findWithoutProperties} - to determine if any objects do not have a set of properties
|
|
787
|
+
* @see {@link module:file.keys|keys} - to get a list of unique properties of all objects in a list.
|
|
788
|
+
* @example
|
|
789
|
+
* const students = [
|
|
790
|
+
* { first: 'john', last: 'doe', birthday: '2002-04-01' },
|
|
791
|
+
* { first: 'jane', last: 'doe', birthday: '2003-05-01' },
|
|
792
|
+
* { first: 'jack', last: 'white', failure: 401 }
|
|
793
|
+
* ];
|
|
794
|
+
*
|
|
795
|
+
* utils.object.setPropertyDefaults(students, {
|
|
796
|
+
* first: '',
|
|
797
|
+
* last: '',
|
|
798
|
+
* birthday: ''
|
|
799
|
+
* });
|
|
800
|
+
*
|
|
801
|
+
* // [
|
|
802
|
+
* // { first: 'john', last: 'doe', birthday: '2002-04-01' },
|
|
803
|
+
* // { first: 'jane', last: 'doe', birthday: '2003-05-01' },
|
|
804
|
+
* // { first: 'jack', last: 'white', birthday: '', failure: 401 }
|
|
805
|
+
* // ];
|
|
806
|
+
*/
|
|
807
|
+
module.exports.setPropertyDefaults = function setPropertyDefaults(targetObject, defaultObj) {
|
|
808
|
+
const cleanTargets = Array.isArray(targetObject)
|
|
809
|
+
? targetObject
|
|
810
|
+
: [targetObject];
|
|
811
|
+
|
|
812
|
+
if (!defaultObj || typeof defaultObj !== 'object') {
|
|
813
|
+
throw Error('object.setPropertyDefaults(targetObject, defaultObject): defaultObject is expected to be an object with properties set to the defaults to apply');
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
const defaultKeys = Object.getOwnPropertyNames(defaultObj);
|
|
817
|
+
|
|
818
|
+
cleanTargets.forEach((target) => {
|
|
819
|
+
defaultKeys.forEach((prop) => {
|
|
820
|
+
if (typeof target[prop] === 'undefined') {
|
|
821
|
+
target[prop] = defaultObj[prop];
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
};
|