jupyter-ijavascript-utils 1.9.0 → 1.10.0
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 +4 -0
- package/package.json +2 -2
- package/src/TableGenerator.js +67 -8
- package/src/aggregate.js +197 -0
- package/src/file.js +86 -0
- package/src/format.js +41 -0
- package/src/ijs.js +1 -1
- 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
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
<img src="https://img.shields.io/badge/License-MIT-green" />
|
|
7
7
|
</a>
|
|
8
8
|
<img src="https://img.shields.io/badge/Coverage-98-green" />
|
|
9
|
+
<a href="https://github.com/paulroth3d/jupyter-ijavascript-utils" alt="npm">
|
|
10
|
+
<img src="https://img.shields.io/badge/npm-%5E1.10.0-red" />
|
|
11
|
+
</a>
|
|
9
12
|
</p>
|
|
10
13
|
|
|
11
14
|
# Overview
|
|
@@ -18,6 +21,7 @@ See documentation at: [https://jupyter-ijavascript-utils.onrender.com/](https://
|
|
|
18
21
|
|
|
19
22
|
# What's New
|
|
20
23
|
|
|
24
|
+
* 1.10 - provide percentile (like 50th percentile) aggregates
|
|
21
25
|
* 1.9 - allow transposing results on TableGenerator.
|
|
22
26
|
* 1.8 - add in What can I Do tutorial, and object.join methods
|
|
23
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.0",
|
|
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
|
|
|
@@ -452,20 +452,64 @@ class TableGenerator {
|
|
|
452
452
|
*
|
|
453
453
|
* (This is an alternate to {@link formatterFn} or simple `.map()` call on the source data)
|
|
454
454
|
*
|
|
455
|
+
* **NOTE: Only matching properties on the formatter object are changed - all others are left alone.**
|
|
456
|
+
*
|
|
455
457
|
* For example:
|
|
456
458
|
*
|
|
457
459
|
* ```
|
|
458
|
-
* data = [
|
|
460
|
+
* data = [
|
|
461
|
+
* {station: 'A', temp: 98, type: 'F', descr: '0123'},
|
|
462
|
+
* {station: 'A', temp: 99, type: 'F', descr: '0123456'},
|
|
463
|
+
* {station: 'A', temp: 100, type: 'F', descr: '0123456789'}
|
|
464
|
+
* ];
|
|
459
465
|
*
|
|
460
466
|
* //-- simple example where the temp property is converted, and type property overwritten
|
|
461
467
|
* new TableGenerator(data)
|
|
462
468
|
* .formatter({
|
|
469
|
+
* //-- property 'station' not mentioned, so no change
|
|
470
|
+
*
|
|
471
|
+
* //-- convert temperature to celsius
|
|
463
472
|
* temp: (value) => (value - 32) * 0.5556,
|
|
464
|
-
* type
|
|
465
|
-
*
|
|
473
|
+
* //-- overwrite type from 'F' to 'C'
|
|
474
|
+
* type: 'C',
|
|
475
|
+
* //-- ellipsify to shorten the description string, if longer than 8 characters
|
|
476
|
+
* descr: (str) => utils.format.ellipsify(str, 8)
|
|
477
|
+
* }).renderMarkdown()
|
|
466
478
|
* ```
|
|
467
479
|
*
|
|
468
|
-
*
|
|
480
|
+
* station|temp |type|descr
|
|
481
|
+
* -- |-- |-- |--
|
|
482
|
+
* A |36.67 |F |0123
|
|
483
|
+
* A |37.225|F |0123456
|
|
484
|
+
* A |37.781|F |01234567…
|
|
485
|
+
*
|
|
486
|
+
* Note, due to frequent requests, simple datatype conversions can be requested.
|
|
487
|
+
*
|
|
488
|
+
* Only ('String', 'Number', and 'Boolean') are supported
|
|
489
|
+
*
|
|
490
|
+
* ```
|
|
491
|
+
* data = [
|
|
492
|
+
* { propA: ' 8009', propB: 8009, isBoolean: 0},
|
|
493
|
+
* { propA: ' 92032', propB: 92032, isBoolean: 1},
|
|
494
|
+
* { propA: ' 234234', propB: 234234, isBoolean: 1},
|
|
495
|
+
* ];
|
|
496
|
+
*
|
|
497
|
+
* new utils.TableGenerator(data)
|
|
498
|
+
* .formatter({
|
|
499
|
+
* //-- convert Prop A to Number - so render with Locale Number Formatting
|
|
500
|
+
* propA: 'number',
|
|
501
|
+
* //-- conver PropB to String - so render without Locale Number Formatting
|
|
502
|
+
* propB: 'string',
|
|
503
|
+
* //-- render 'True' or 'False'
|
|
504
|
+
* isBoolean: 'boolean'
|
|
505
|
+
* }).renderMarkdown();
|
|
506
|
+
* ```
|
|
507
|
+
*
|
|
508
|
+
* propA|propB|isBoolean
|
|
509
|
+
* -- |-- |--
|
|
510
|
+
* 8,009 |8009 |false
|
|
511
|
+
* 92,032 |92032 |true
|
|
512
|
+
* 234,234 |234234 |true
|
|
469
513
|
*
|
|
470
514
|
* @param {Object} obj - object with properties storing arrow functions
|
|
471
515
|
* @param {Function} obj.PropertyToTranslate - (value) => result
|
|
@@ -480,10 +524,25 @@ class TableGenerator {
|
|
|
480
524
|
|
|
481
525
|
const fnMap = new Map();
|
|
482
526
|
Object.getOwnPropertyNames(obj).forEach((key) => {
|
|
483
|
-
if ((typeof obj[key])
|
|
484
|
-
|
|
527
|
+
if ((typeof obj[key]) === 'string') {
|
|
528
|
+
let fn;
|
|
529
|
+
const str = obj[key].toLowerCase();
|
|
530
|
+
if (str === 'string') {
|
|
531
|
+
fn = (val) => String(val);
|
|
532
|
+
} else if (str === 'number') {
|
|
533
|
+
fn = (val) => Number(val);
|
|
534
|
+
} else if (str === 'boolean') {
|
|
535
|
+
fn = (val) => val ? 'true' : 'false';
|
|
536
|
+
} else {
|
|
537
|
+
throw Error(`TableGenerator.format: property ${key} formatter of ${str} is unsupported. Only (String, Number, Boolean) are supported`);
|
|
538
|
+
}
|
|
539
|
+
fnMap.set(key, fn);
|
|
540
|
+
} else {
|
|
541
|
+
if ((typeof obj[key]) !== 'function') {
|
|
542
|
+
throw (Error(`Formatter properties must be functions. [${key}]`));
|
|
543
|
+
}
|
|
544
|
+
fnMap.set(key, obj[key]);
|
|
485
545
|
}
|
|
486
|
-
fnMap.set(key, obj[key]);
|
|
487
546
|
});
|
|
488
547
|
|
|
489
548
|
this.#formatterFn = ({ value, property }) => fnMap.has(property)
|
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/ijs.js
CHANGED
|
@@ -135,7 +135,7 @@ module.exports.await = async function ijsAsync(fn) {
|
|
|
135
135
|
context.$$.async();
|
|
136
136
|
|
|
137
137
|
try {
|
|
138
|
-
const results = fn(context.$$, context.console);
|
|
138
|
+
const results = await fn(context.$$, context.console);
|
|
139
139
|
context.$$.sendResult(results);
|
|
140
140
|
} catch (err) {
|
|
141
141
|
context.console.error('error occurred');
|
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
|
+
};
|