jupyter-ijavascript-utils 1.9.2 → 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 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/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.9.2-red" />
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.9.2",
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",
@@ -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 = [{temp: 98, type: 'F'}, {temp: 99, type: 'F'}, {temp: 100, type: 'F'}];
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: (value) => 'C'
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
- * Only properties on the object are checked - all others are left alone.
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]) !== 'function') {
484
- throw (Error(`Formatter properties must be functions. [${key}]`));
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/format.js CHANGED
@@ -12,6 +12,7 @@
12
12
  * * Formatting Strings
13
13
  * * {@link module:format.capitalize|format.capitalize} - Capitalizes only the first character in the string (ex: 'John paul');
14
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'
15
16
  * * Formatting Time
16
17
  * * {@link module:format.millisecondDuration|format.millisecondDuration}
17
18
  * * Mapping Values
package/src/object.js CHANGED
@@ -22,6 +22,7 @@ const schemaGenerator = require('generate-schema');
22
22
  * * {@link module:object.fetchObjectProperties|fetchObjectProperties(object, string[])} - use dot notation to bring multiple child properties onto a parent
23
23
  * * {@link module:object.join|join(array, index, map, fn)} - join a collection against a map by a given index
24
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
25
26
  * * Rename properties
26
27
  * * {@link module:object.cleanProperties|cleanProperties()} - correct inaccessible property names in a list of objects
27
28
  * * {@link module:object.cleanPropertyNames|cleanPropertyNames()} - create a translation of inaccessible names to accessible ones
@@ -638,6 +639,40 @@ module.exports.joinProperties = function join(objectArray, indexField, targetMap
638
639
  return ObjectUtils.join(objectArray, indexField, targetMap, joinFn);
639
640
  };
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
+
641
676
  /**
642
677
  * Finds objects that do not have ALL the properties specified.
643
678
  *