jupyter-ijavascript-utils 1.43.0 → 1.45.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
@@ -75,6 +75,7 @@ Give it a try here:
75
75
 
76
76
  ## What's New
77
77
 
78
+ * 1.45 - more ways to understand the data - {@link module:aggregate.coalesce|aggregate.coalesce()}, convert properties to arrow/dot notation / reverse it {@link module:object.flatten|object.flatten()} / {@link module:object.expand|object.expand()} and {@link module:object.isObject|object.isObject()}
78
79
  * 1.43 - esm module fix since still not supported yet in ijavascript
79
80
  * 1.41 - {@link module:object.propertyInherit|object.propertyInherit} - to simplify inheriting values from one record to the next
80
81
  * 1.40 - {@link module:array.extract|array.extract} and {@link module:array.applyArrayValues|array.applyArrayValues} to allow for extracting values from arrays, transforming them on a separate process and applying them deeply and safely
package/Dockerfile CHANGED
@@ -1,3 +1,3 @@
1
1
  # syntax=docker/dockerfile:1
2
2
 
3
- FROM darkbluestudios/jupyter-ijavascript-utils:binder_1.43.0
3
+ FROM darkbluestudios/jupyter-ijavascript-utils:binder_1.45.0
package/README.md CHANGED
@@ -54,7 +54,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
54
54
  ![Screenshot of example notebook](docResources/img/mainExampleNotebook.png)
55
55
 
56
56
  # What's New
57
-
57
+ * 1.45 - more ways to understand the data - aggregate.coalesce(), convert properties to arrow/dot notation / reverse it : object.flatten() / object.expand() and Object.isObject()
58
58
  * 1.43 - esm module fix since still not supported yet in ijavascript
59
59
  * 1.41 - object.propertyInherit - to simplify inheriting values from one record to the next
60
60
  * 1.40 - array.extract and array.applyArrayValue to allow for extracting values from arrays, transforming them on a separate process and applying them back
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyter-ijavascript-utils",
3
- "version": "1.43.0",
3
+ "version": "1.45.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",
@@ -17,7 +17,8 @@
17
17
  "test:debug": "TZ=UTC node --inspect-brk node_modules/jest/bin/jest.js --runInBand",
18
18
  "test:coverage": "TZ=UTC jest src --collectCoverage",
19
19
  "test:watch:coverage": "TZ=UTC jest src --watch --collectCoverage",
20
- "doc:taffy": "npm install taffydb && npm run doc",
20
+ "doc:taffy": "cp -R tmp/taffydb node_modules",
21
+ "doc:taffy:install": "npm install taffydb && npm run doc",
21
22
  "docs": "npm run doc",
22
23
  "doc": "npm run prep:docdash && node_modules/.bin/jsdoc -c ./jsdoc.json -u ./tutorials ./DOCS.md",
23
24
  "prep:docdash": "cp docResources/docdash/layout.tmpl node_modules/docdash/tmpl/layout.tmpl && rm -rf docResources/notebooks/node_modules",
package/src/aggregate.js CHANGED
@@ -38,6 +38,7 @@ const FormatUtils = require('./format');
38
38
  * * {@link module:aggregate.length|length()} - Number of records found in the collection
39
39
  * * {@link module:aggregate.first|first()} - returns first non-null/undefined in list
40
40
  * * {@link module:aggregate.sum|sum()} - sum of a collection
41
+ * * {@link module:aggregate.coalesce|coalesce()} - given a list of objects, creates a single object with first non-null value of all properties
41
42
  * * Functional
42
43
  * * {@link module:aggregate.deferCollection|deferCollection(function, bindArg, bindArg, ...)} - bind a function with arguments
43
44
  * * Percentile
@@ -389,6 +390,87 @@ module.exports.sum = function sum(collection, accessor) {
389
390
  return collection.reduce((current, val) => current + cleanedFunc(val), 0);
390
391
  };
391
392
 
393
+ module.exports.coalesceDefaultEvaluationFn = function coalesceDefaultEvaluationFn(
394
+ entryValue,
395
+ currentCoalescedValue,
396
+ entryPropName,
397
+ entry
398
+ ) {
399
+ if (currentCoalescedValue) return false;
400
+ if (entryValue == null) return false;
401
+ if (entryValue === 0) return false;
402
+ if (Array.isArray(entryValue) && entryValue.length === 0) return false;
403
+ if (entryValue instanceof Date && entryValue.getTime() === 0) return false;
404
+ return true;
405
+ };
406
+
407
+ /**
408
+ * Coalesces a collection of objects to return a single object that has the first non-null value
409
+ * of all unique properties found in the collection.
410
+ *
411
+ * example:
412
+ *
413
+ * ```
414
+ * collection = [
415
+ * { first: 'john' },
416
+ * { last: 'doe' },
417
+ * { age: 23 }
418
+ * ];
419
+ * utils.agg.coalesce(collection);
420
+ * // { first: 'john', last: 'doe', age: 23 };
421
+ * ```
422
+ *
423
+ * this also works to show example values for a large number of objects
424
+ *
425
+ * ```
426
+ * collection = [
427
+ * { first: 'john', last: 'doe', age: 23, failedClass: null },
428
+ * { first: 'jane', last: 'doe', favouriteColor: 'blue', failedClass: null },
429
+ * null,
430
+ * { first: 'bill', favouriteColor: 'red', failedClass: 'asbx-dx2' }
431
+ * ];
432
+ * utils.agg.coalesce(collection);
433
+ * //-- now we can understand the types of values we got for each property type
434
+ * // { first: 'john', last: 'doe', age: 23, favouriteColor: 'blue', failedClass: 'asbx-dx2' }
435
+ * ```
436
+ *
437
+ * Note - an optional evaluationFn can be provided, that can be used to determine
438
+ * if a value is collected.
439
+ *
440
+ * ```
441
+ * collection = [{ val: null }, { val: 23 }, { val: 2 }, { val: 100 }];
442
+ * maxCoalesce = (val, current) => val && (!current || val > current);
443
+ * utils.agg.coalesce(collection, maxCoalesce);
444
+ * // { val: 100 }
445
+ * ```
446
+ *
447
+ * @param {Array} collection -
448
+ * @param {Function} [evaluationFn] - optional function that defines the value collected
449
+ * - function(entryValue:any, currentCoalescedValue:any, entryPropName:string, entry:Object):Boolean
450
+ * @returns {Object} - Object with all properties found, and the first
451
+ * @see {@link module:describe.describeObjects|describe.describeObjects(collection, options)} - to better understand values found
452
+ */
453
+ module.exports.coalesce = function coalesce(collection, evaluationFn) {
454
+ if (!Array.isArray(collection)) return collection;
455
+ const cleanEvalFn = evaluationFn || AggregateUtils.coalesceDefaultEvaluationFn;
456
+
457
+ const result = {};
458
+
459
+ collection.forEach((entry) => {
460
+ if (ObjectUtils.isObject(entry)) {
461
+ Object.keys(entry).forEach((key) => {
462
+ const entryValue = entry[key];
463
+ const currentCoalescedValue = result[key];
464
+ if (cleanEvalFn(entryValue, currentCoalescedValue, key, entry)) {
465
+ result[key] = entryValue;
466
+ }
467
+ });
468
+ }
469
+ });
470
+
471
+ return result;
472
+ };
473
+
392
474
  /**
393
475
  * The difference between the lowest and the highest values in the collection
394
476
  *
package/src/describe.js CHANGED
@@ -14,6 +14,12 @@ const ObjectUtils = require('./object');
14
14
  * * {@link module:describe.describeNumbers|describeNumbers(collection, options)} - describes a series of numbers
15
15
  * * {@link module:describe.describeDates|describeDates(collection, options)} - describes a series of dates
16
16
  *
17
+ * Most commonly, {@link module:describe.describeObjects|object.describeObjects(collection, options)} is used -
18
+ * as it describes with the appropriate type for each property.
19
+ *
20
+ * Note, if there are multiple child objects within the collection, {@link module:object.flatten|object.flatten()}
21
+ * will bring those values down through dot notation (similar to arrow format) - so they can be better described.
22
+ *
17
23
  * @module describe
18
24
  * @exports describe
19
25
  */
@@ -500,6 +506,9 @@ class DateDescription extends SeriesDescription {
500
506
  * age |number|3 |25 |23 |24 | | |
501
507
  * enrolled|Date |3 |2022-01-05T00:00:00.000Z|2022-01-01T00:00:00.000Z|2022-01-03T00:00:00.000Z| | |
502
508
  *
509
+ * Note, if there are multiple child objects within the collection, {@link module:object.flatten|object.flatten()}
510
+ * will bring those values down through dot notation (similar to arrow format) - so they can be better described.
511
+ *
503
512
  * @param {Object[]} collection - Collection of objects to be described
504
513
  * @param {Object} options - options to be used
505
514
  * @param {String[]} options.include - string list of fields to include in the description
@@ -508,6 +517,7 @@ class DateDescription extends SeriesDescription {
508
517
  * - that will override how that property is parsed.
509
518
  * @param {Number} maxRows - max rows to consider before halting
510
519
  * @returns {SeriesDescription[]} - collection of descriptions - one for each property
520
+ * @see {@link module:object.flatten|object.flatten()} - if the collection of objects have a large number of child objects.
511
521
  */
512
522
  module.exports.describeObjects = function describeObjects(collection, options) {
513
523
  const cleanCollection = Array.isArray(collection) ? collection : [collection];
package/src/object.js CHANGED
@@ -8,6 +8,7 @@ const FormatUtils = require('./format');
8
8
  * Utility for working with and massaging javascript objects.
9
9
  *
10
10
  * * Describe objects
11
+ * * {@link module:object.isObject|isObject()} - Determine if a given value is an Object and not a Number, String, Array or Date
11
12
  * * {@link module:object.keys|keys()} - Safely get the keys of an object or list of objects
12
13
  * * {@link module:object.getObjectPropertyTypes|getObjectPropertyTypes()} - describe the properties of a list of objects
13
14
  * * {@link module:object.generateSchema|generateSchema()} - generate a schema / describe properties of a list of objects
@@ -45,8 +46,9 @@ const FormatUtils = require('./format');
45
46
  * * {@link module:object.cleanPropertyName|cleanPropertyName()} - create a translation of a specific property name to be accessible.
46
47
  * * {@link module:object.renameProperties|renameProperties()} - Use a translation from old property names to new ones
47
48
  * * Flatten object properties
48
- * * {@link module:object.collapseSpecificObject|collapseSpecificObject()} - flatten object properties
49
- * * {@link module:object.collapse|collapse()} - flatten specific object
49
+ * * {@link module:object.collapse|collapse()} - coalesce properties from all nested objects to the base object.
50
+ * * {@link module:object.flatten|flatten()} - creates dot notation properties (similar to arrow notation) of all child objects.
51
+ * * {@link module:object.expand|expand()} - expands dot notation properties onto sub children (inverse of flatten)
50
52
  * * Create Map of objects by key
51
53
  * * {@link module:object.mapByProperty|mapByProperty()} -
52
54
  * * {@link module:group.by|group(collection, accessor)}
@@ -463,12 +465,138 @@ const collapseSpecificObject = function collapseSpecificObject(sourceObj, target
463
465
  * // 'Hi John, how do you like your F150?
464
466
  * @param {Object} objectTree
465
467
  * @returns {Object} - object with all the properties added
466
- * @see #MAX_COLLAPSE_DEPTH -
468
+ * @see #MAX_COLLAPSE_DEPTH - library property that defines how far to collapse
467
469
  */
468
470
  module.exports.collapse = function collapse(targetObj) {
469
471
  return collapseSpecificObject({}, targetObj, 0);
470
472
  };
471
473
 
474
+ /**
475
+ * Determines whether a value is an Object and not an Array or a Date
476
+ * @param {any} testValue - value to be tested
477
+ * @returns {Boolean} - whether the testValue is an Object and not an Array or a Date.
478
+ */
479
+ module.exports.isObject = (o) => o != null
480
+ && typeof o === 'object'
481
+ && !Array.isArray(o)
482
+ && !(o instanceof Date);
483
+
484
+ /**
485
+ * While originally intended as a sub-implementation for Flatten,
486
+ * this was exposed in case additional cases ever arose.
487
+ *
488
+ * Example:
489
+ *
490
+ * ```
491
+ * student = { first: 'john', last: 'doe' };
492
+ * friend = { first: 'jane', last: 'doe' };
493
+ * course = { id: 'econ-101', professor: { id: 10101, first: 'jim', last: 'gifford' }};
494
+ * flattenedObj = {};
495
+ * flattenedObj = utils.object.flattenObjectOntoAnother(student, flattenedObj);
496
+ * // { first: 'john', last: 'doe' }
497
+ * flattenedObj = utils.object.flattenObjectOntoAnother(friend, flattenedObj, 'friend.');
498
+ * // { first: 'john', last: 'doe', 'friend.first': 'jane', 'friend.last': 'doe' };
499
+ * flattenedObj = utils.object.flattenObjectOntoAnother(course, flattenedObj, 'course.');
500
+ * // { first: 'john', last: 'doe', 'friend.first': 'jane', 'friend.last': 'doe', 'course.id': 'econ-101',
501
+ * // 'course.professor.id': 10101, 'course.professor.first': 'jim', 'course.professor.last': 'gifford' };
502
+ * ```
503
+ *
504
+ * See flatten for a an alternative to achieve the same result.
505
+ *
506
+ * @param {Object} sourceObj - The object to review for source values / properties
507
+ * @param {Object} [targetObj={}] - The object to apply the dot notation properties onto
508
+ * @param {String} [prefix=''] - the string prefix of any properties found on source, to apply onto target
509
+ * @returns {Object} - the targetObj with the properties applied (in place)
510
+ */
511
+ module.exports.flattenObjectOntoAnother = function flattenObjectOntoAnother(sourceObj, targetObj, prefix) {
512
+ const cleanTarget = targetObj || {};
513
+ const cleanPrefix = prefix || '';
514
+
515
+ if (!ObjectUtils.isObject(sourceObj)) {
516
+ return sourceObj;
517
+ }
518
+
519
+ const sourceKeys = ObjectUtils.keys(sourceObj);
520
+
521
+ sourceKeys.forEach((key) => {
522
+ const prefixedKey = `${cleanPrefix}${key}`;
523
+ const keyValue = sourceObj[key];
524
+ if (ObjectUtils.isObject(keyValue)) {
525
+ ObjectUtils.flattenObjectOntoAnother(keyValue, cleanTarget, `${prefixedKey}.`);
526
+ } else {
527
+ cleanTarget[prefixedKey] = keyValue;
528
+ }
529
+ });
530
+ return cleanTarget;
531
+ };
532
+
533
+ /**
534
+ * Flattens an object and sub-objects into dot notation - to have easier time understanding schemas and explainations.
535
+ *
536
+ * example:
537
+ *
538
+ * ```
539
+ * student = {
540
+ * first: 'john', last: 'doe',
541
+ * friend: { first: 'jane', last: 'doe' },
542
+ * course: { id: 'econ-101', professor: { id: 10101, first: 'jim', last: 'gifford' }}
543
+ * };
544
+ *
545
+ * flattenedObj = utils.object.flatten(student);
546
+ * // {
547
+ * // first: 'john', last: 'doe',
548
+ * // 'friend.first': 'jane', 'friend.last': 'doe',
549
+ * // 'course.id': 'econ-101',
550
+ * // 'course.professor.id': 10101, 'course.professor.first': 'jim', 'course.professor.last': 'gifford'
551
+ * // };
552
+ * ```
553
+ * @param {Object} targetObj - Object with all properties and sub-objects to flatten.
554
+ * @returns {Object} - New object with dot notation properties
555
+ * @see {@link module:object.expand|expand()} - as the inverse
556
+ * @see {@link module:describe.describeObjects|describeObjects(collection, options)} - as a way to describe the values provided
557
+ */
558
+ module.exports.flatten = function flatten(targetObj) {
559
+ return ObjectUtils.flattenObjectOntoAnother(targetObj);
560
+ };
561
+
562
+ /**
563
+ * The inverse of Flatten - this takes an object with dot notation properties,
564
+ * and creates the sub-objects as necessary to contain the properties defined.
565
+ *
566
+ * Example:
567
+ *
568
+ * ```
569
+ * flattenedObj = {
570
+ * first: 'john', last: 'doe',
571
+ * 'friend.first': 'jane', 'friend.last': 'doe',
572
+ * 'course.id': 'econ-101',
573
+ * 'course.professor.id': 10101, 'course.professor.first': 'jim', 'course.professor.last': 'gifford'
574
+ * };
575
+ *
576
+ * expandedObj = utils.object.expand(flattenedObj);
577
+ * // {
578
+ * // first: 'john', last: 'doe',
579
+ * // friend: { first: 'jane', last: 'doe' },
580
+ * // course: { id: 'econ-101', professor: { id: 10101, first: 'jim', last: 'gifford' }}
581
+ * // };
582
+ * ```
583
+ *
584
+ * @param {Object} targetObj - a flattened object (with dot notation properties) to be expanded
585
+ * @returns {Object} - a new object with sub-objects for each of the dot-notation entries
586
+ * @see {@link module:object.flatten|flatten()} - as the inverse
587
+ */
588
+ module.exports.expand = function expand(targetObj) {
589
+ if (!ObjectUtils.isObject(targetObj)) return targetObj;
590
+
591
+ const result = {};
592
+ const keys = ObjectUtils.keys(targetObj);
593
+ keys.forEach((key) => {
594
+ ObjectUtils.applyPropertyValue(result, key, targetObj[key]);
595
+ });
596
+
597
+ return result;
598
+ };
599
+
472
600
  /**
473
601
  * Keeps only specific properties on an object or list of objects
474
602
  * @param {Object | Object[]} list - collection of objects to filter
@@ -1429,9 +1557,11 @@ module.exports.mapProperties = function mapProperties(objCollection, formattingF
1429
1557
  ? [objCollection]
1430
1558
  : objCollection;
1431
1559
 
1432
- const cleanProperties = propertiesToFormat.length > 0 && Array.isArray(propertiesToFormat[0])
1433
- ? propertiesToFormat[0]
1434
- : propertiesToFormat;
1560
+ const cleanProperties = propertiesToFormat.length === 0
1561
+ ? ObjectUtils.keys(objCollection)
1562
+ : propertiesToFormat.length > 0 && Array.isArray(propertiesToFormat[0])
1563
+ ? propertiesToFormat[0]
1564
+ : propertiesToFormat;
1435
1565
 
1436
1566
  if (typeof formattingFn !== 'function') {
1437
1567
  throw Error('object.mapProperties(collection, formattingFn, ...propertiesToFormat): formattingFn must be provided');
package/src/set.js CHANGED
@@ -27,11 +27,13 @@ const SetUtils = module.exports;
27
27
  /**
28
28
  * Mutably adds a value to a set, and then returns the set. (Allowing Chaining)
29
29
  *
30
- * (If you wish to immutably, use ES6: `{...setA, value1, value2, ...setB, etc...}`)
30
+ * (If you wish to immutably, use ES6: `{...setA, value1, value2, ...setB, etc...}`<br />
31
+ * or use the {@link module:set.union|union(set, list|set|iterable)} command below)
31
32
  *
32
33
  * @param {set} setTarget - set to add values to
33
34
  * @param {any} val - value to add to the set
34
35
  * @returns {set} setTarget
36
+ * @see {@link module:set.union|union(set, list|set|iterable)}
35
37
  * @example
36
38
  * setA = new Set([1, 2, 3]);
37
39
  * utils.array.add(setA, 4, 5, 6); // Set([1, 2, 3, 4, 5, 6])
@@ -50,7 +52,8 @@ module.exports.add = function add(setTarget, ...rest) {
50
52
  *
51
53
  * @param {set} setTarget - set to add values to
52
54
  * @param {iteratable} iteratable - iteratable that can be unioned into the set.
53
- * @returns {set} setTarget
55
+ * @param {...iteratable} rest - additional iteratables to add to the union
56
+ * @returns {set} new set that contains values from all sources
54
57
  * @example
55
58
  *
56
59
  * setA = new Set([1, 2, 3]);
@@ -61,8 +64,12 @@ module.exports.add = function add(setTarget, ...rest) {
61
64
  * listB = [4, 5, 6];
62
65
  * array.union(setA, listB) // Set([1, 2, 3, 4, 5, 6])
63
66
  *
67
+ * setC = new Set([7, 8, 9]);
68
+ * array.union(setA, listB, setC);
69
+ * // Set(1, 2, 3, 4, 5, 6, 7, 8, 9);
70
+ *
64
71
  */
65
- module.exports.union = function union(setTarget, iteratable) {
72
+ module.exports.union = function union(setTarget, iteratable, ...rest) {
66
73
  const target = setTarget instanceof Set ? setTarget : new Set(setTarget);
67
74
  if (iteratable) {
68
75
  // eslint-disable-next-line
@@ -70,6 +77,7 @@ module.exports.union = function union(setTarget, iteratable) {
70
77
  target.add(v);
71
78
  }
72
79
  }
80
+ if (rest.length > 0) return SetUtils.union.apply(this, [target, ...rest]);
73
81
  return target;
74
82
  };
75
83
 
@@ -80,7 +88,8 @@ module.exports.union = function union(setTarget, iteratable) {
80
88
  *
81
89
  * @param {Set} sourceA - the set to check for common items
82
90
  * @param {Set} sourceB - another set to check for common items
83
- * @returns {Set} - set of items that are in both sourceA and sourceB
91
+ * @param {...iteratable} rest - additional iteratables to verify
92
+ * @returns {Set} - set of items that are in all sources
84
93
  * @example
85
94
  * setA = new Set([1, 2, 3, 4]);
86
95
  * setB = new Set([3, 4, 5, 6]);
@@ -88,10 +97,14 @@ module.exports.union = function union(setTarget, iteratable) {
88
97
  *
89
98
  * // Note that you can use other iteratable things too
90
99
  * utils.set.intersection([1, 2, 3, 4], [3, 4, 5, 6]); // Set([3, 4])
100
+ *
101
+ * setC = new Set([3, 4, 9, 10]);
102
+ * utils.set.intersection(setA, setB, setC); // Set([3, 4]);
91
103
  */
92
- module.exports.intersection = function intersection(sourceA, sourceB) {
104
+ module.exports.intersection = function intersection(sourceA, sourceB, ...rest) {
93
105
  const targetA = sourceA instanceof Set ? sourceA : new Set(sourceA);
94
106
  const results = new Set([...sourceB].filter((val) => targetA.has(val)));
107
+ if (rest.length > 0) return SetUtils.intersection.apply(this, [results, ...rest]);
95
108
  return results;
96
109
  };
97
110