jupyter-ijavascript-utils 1.31.0 → 1.33.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 +2 -0
- package/Dockerfile +1 -1
- package/README.md +2 -0
- package/package.json +1 -1
- package/src/array.js +184 -0
- package/src/object.js +188 -2
package/DOCS.md
CHANGED
|
@@ -75,6 +75,8 @@ Give it a try here:
|
|
|
75
75
|
|
|
76
76
|
## What's New
|
|
77
77
|
|
|
78
|
+
* 1.33 - Object.augmentInherit and Object.union
|
|
79
|
+
* 1.32 - Array.indexify to identify sections within a 1d array into a hierarchy.
|
|
78
80
|
* 1.31 - harden Array.transpose for arrays with nulls, and Table.generateTSV
|
|
79
81
|
* 1.30 - add Format.wordWrap and Format.lineCount
|
|
80
82
|
* 1.29 - Updated TableGenerator.format method
|
package/Dockerfile
CHANGED
package/README.md
CHANGED
|
@@ -55,6 +55,8 @@ This is not intended to be the only way to accomplish many of these tasks, and a
|
|
|
55
55
|
|
|
56
56
|
# What's New
|
|
57
57
|
|
|
58
|
+
* 1.33 - Object.augmentInherit and Object.union
|
|
59
|
+
* 1.32 - Array.indexify to identify sections within a 1d array into a hierarchy.
|
|
58
60
|
* 1.31 - harden Array.transpose for arrays with nulls, and Table.generateTSV
|
|
59
61
|
* 1.30 - add Format.wordWrap and Format.lineCount
|
|
60
62
|
* 1.29 - Updated TableGenerator.format method
|
package/package.json
CHANGED
package/src/array.js
CHANGED
|
@@ -17,6 +17,7 @@ require('./_types/global');
|
|
|
17
17
|
* * {@link module:array.createSort|array.createSort(sortIndex, sortIndex, ...)} - generates a sorting function
|
|
18
18
|
* * {@link module:array.SORT_ASCENDING|array.SORT_ASCENDING} - common ascending sorting function for array.sort()
|
|
19
19
|
* * {@link module:array.SORT_DESCENDING|array.SORT_DESCENDING} - common descending sorting function for array.sort()
|
|
20
|
+
* * {@link module:array.indexify|array.indexify} - identify sections within a 1d array to create a hierarchy.
|
|
20
21
|
* * Rearrange Array
|
|
21
22
|
* * {@link module:array.reshape|array.reshape} - reshapes an array to a size of rows and columns
|
|
22
23
|
* * {@link module:array.transpose|array.transpose} - transposes (flips - the array along the diagonal)
|
|
@@ -504,3 +505,186 @@ module.exports.arrangeMulti = function arangeMulti(...dimensions) {
|
|
|
504
505
|
return ArrayUtils.size(currentDimension, () => ArrayUtils.clone(childDimensionalValue));
|
|
505
506
|
};
|
|
506
507
|
module.exports.arangeMulti = module.exports.arrangeMulti;
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Create a unique number index for each element in an array,
|
|
511
|
+
* alternatively using additional functions to indicate hierarchies of data.
|
|
512
|
+
*
|
|
513
|
+
* For example, markdown can be considered a hierarchy of data:
|
|
514
|
+
*
|
|
515
|
+
* ```
|
|
516
|
+
* markdownList = `# Overview
|
|
517
|
+
* This entire list is a hierarchy of data.
|
|
518
|
+
*
|
|
519
|
+
* # Section A
|
|
520
|
+
* This describes section A
|
|
521
|
+
*
|
|
522
|
+
* ## SubSection 1
|
|
523
|
+
* With a subsection belonging to Section A
|
|
524
|
+
*
|
|
525
|
+
* ## SubSection 2
|
|
526
|
+
* And another subsection sibling to SubSection 1, but also under Section A.
|
|
527
|
+
*
|
|
528
|
+
* # Section B
|
|
529
|
+
* With an entirely unrelated section B, that is sibling to Section A
|
|
530
|
+
*
|
|
531
|
+
* ## SubSection 1
|
|
532
|
+
* And another subsection 1, but this time related to Section B.`;
|
|
533
|
+
* ```
|
|
534
|
+
*
|
|
535
|
+
* And we want to convert this 1d array into a hierarchy.
|
|
536
|
+
*
|
|
537
|
+
* ```
|
|
538
|
+
* data = markdownList.split('\n')
|
|
539
|
+
* .filter(line => line ? true : false); // check for empty lines
|
|
540
|
+
*
|
|
541
|
+
* utils.format.consoleLines( data, 4);
|
|
542
|
+
* // ['# Overview',
|
|
543
|
+
* // 'This entire list is a hierarchy of data.',
|
|
544
|
+
* // '# Section A',
|
|
545
|
+
* // 'This describes section A',;
|
|
546
|
+
*
|
|
547
|
+
* //-- functions that return True if we are in a new "group"
|
|
548
|
+
* isHeader1 = (str) => str.startsWith('# ');
|
|
549
|
+
*
|
|
550
|
+
* isHeader1('# Overview'); // true
|
|
551
|
+
* isHeader1('This entire list is a hierarchy of data'); // false
|
|
552
|
+
* isHeader1('# Section A'); // true
|
|
553
|
+
* isHeader1('This describes section A'); // false
|
|
554
|
+
*
|
|
555
|
+
* indexedData = utils.array.indexify(data, isHeader1);
|
|
556
|
+
* [
|
|
557
|
+
* { entry: 'Heading', section: [ 0 ], subIndex: 1 },
|
|
558
|
+
* { entry: '# Overview', section: [ 1 ], subIndex: 0 },
|
|
559
|
+
* {
|
|
560
|
+
* entry: 'This entire list is a hierarchy of data.',
|
|
561
|
+
* section: [ 1 ],
|
|
562
|
+
* subIndex: 1
|
|
563
|
+
* },
|
|
564
|
+
* { entry: '# Section A', section: [ 2 ], subIndex: 0 },
|
|
565
|
+
* { entry: 'This describes section A', section: [ 2 ], subIndex: 1 },
|
|
566
|
+
* { entry: '## SubSection 1', section: [ 2 ], subIndex: 2 },
|
|
567
|
+
* {
|
|
568
|
+
* entry: 'With a subsection belonging to Section A',
|
|
569
|
+
* section: [ 2 ],
|
|
570
|
+
* subIndex: 3
|
|
571
|
+
* },
|
|
572
|
+
* { entry: '## SubSection 2', section: [ 2 ], subIndex: 4 },
|
|
573
|
+
* {
|
|
574
|
+
* entry: 'And another subsection sibling to SubSection 1, but also under Section A.',
|
|
575
|
+
* section: [ 2 ],
|
|
576
|
+
* subIndex: 5
|
|
577
|
+
* },
|
|
578
|
+
* { entry: '# Section B', section: [ 3 ], subIndex: 0 },
|
|
579
|
+
* {
|
|
580
|
+
* entry: 'With an entirely unrelated section B, that is sibling to Section A',
|
|
581
|
+
* section: [ 3 ],
|
|
582
|
+
* subIndex: 1
|
|
583
|
+
* },
|
|
584
|
+
* { entry: '## SubSection 1', section: [ 3 ], subIndex: 2 },
|
|
585
|
+
* {
|
|
586
|
+
* entry: 'And another subsection 1, but this time related to Section B.',
|
|
587
|
+
* section: [ 3 ],
|
|
588
|
+
* subIndex: 3
|
|
589
|
+
* }
|
|
590
|
+
* ];
|
|
591
|
+
* ```
|
|
592
|
+
*
|
|
593
|
+
* Note that this only indexes elements by the first header.
|
|
594
|
+
*
|
|
595
|
+
* To index this with two levels of hierarchy, we can pass another function.
|
|
596
|
+
*
|
|
597
|
+
* ```
|
|
598
|
+
* isHeader2 = (str) => str.startsWith('## ');
|
|
599
|
+
*
|
|
600
|
+
* isHeader2('# Overview'); // false
|
|
601
|
+
* isHeader2('This entire list is a hierarchy of data'); // false
|
|
602
|
+
* isHeader2('# Section A'); // true
|
|
603
|
+
* isHeader2('This describes section A'); // false
|
|
604
|
+
*
|
|
605
|
+
* indexedData = utils.array.indexify(data, isHeader1, isHeader2);
|
|
606
|
+
* // [
|
|
607
|
+
* // { entry: 'Heading', section: [ 0, 0 ], subIndex: 1 },
|
|
608
|
+
* // { entry: '# Overview', section: [ 1, 0 ], subIndex: 0 },
|
|
609
|
+
* // {
|
|
610
|
+
* // entry: 'This entire list is a hierarchy of data.',
|
|
611
|
+
* // section: [ 1, 0 ],
|
|
612
|
+
* // subIndex: 1
|
|
613
|
+
* // },
|
|
614
|
+
* // { entry: '# Section A', section: [ 2, 0 ], subIndex: 0 },
|
|
615
|
+
* // { entry: 'This describes section A', section: [ 2, 0 ], subIndex: 1 },
|
|
616
|
+
* // { entry: '## SubSection 1', section: [ 2, 1 ], subIndex: 0 },
|
|
617
|
+
* // {
|
|
618
|
+
* // entry: 'With a subsection belonging to Section A',
|
|
619
|
+
* // section: [ 2, 1 ],
|
|
620
|
+
* // subIndex: 1
|
|
621
|
+
* // },
|
|
622
|
+
* // { entry: '## SubSection 2', section: [ 2, 2 ], subIndex: 0 },
|
|
623
|
+
* // {
|
|
624
|
+
* // entry: 'And another subsection sibling to SubSection 1, but also under Section A.',
|
|
625
|
+
* // section: [ 2, 2 ],
|
|
626
|
+
* // subIndex: 1
|
|
627
|
+
* // },
|
|
628
|
+
* // { entry: '# Section B', section: [ 3, 0 ], subIndex: 0 },
|
|
629
|
+
* // {
|
|
630
|
+
* // entry: 'With an entirely unrelated section B, that is sibling to Section A',
|
|
631
|
+
* // section: [ 3, 0 ],
|
|
632
|
+
* // subIndex: 1
|
|
633
|
+
* // },
|
|
634
|
+
* // { entry: '## SubSection 1', section: [ 3, 1 ], subIndex: 0 },
|
|
635
|
+
* // {
|
|
636
|
+
* // entry: 'And another subsection 1, but this time related to Section B.',
|
|
637
|
+
* // section: [ 3, 1 ],
|
|
638
|
+
* // subIndex: 1
|
|
639
|
+
* // }
|
|
640
|
+
* // ];
|
|
641
|
+
* ```
|
|
642
|
+
*
|
|
643
|
+
* @param {Array} source - list of values to index
|
|
644
|
+
* @param {...Function} sectionIndicatorFunctions - each function indicates a new section
|
|
645
|
+
* @returns {Object[]} - collection of objects, each with a new section (indicating the layers)
|
|
646
|
+
* and subIndex: unique value in the section (always 0 for header)
|
|
647
|
+
*/
|
|
648
|
+
module.exports.indexify = function indexify(source, ...sectionIndicatorFunctions) {
|
|
649
|
+
const functionSignature = 'indexify(source, ...sectionIndicatorFunctions)';
|
|
650
|
+
|
|
651
|
+
const counters = new Array(sectionIndicatorFunctions.length).fill(0);
|
|
652
|
+
let subIndex = 0;
|
|
653
|
+
// counters[counters.length - 1] = -1;
|
|
654
|
+
|
|
655
|
+
//-- validate inputs
|
|
656
|
+
if (!Array.isArray(source)) {
|
|
657
|
+
throw new Error(`${functionSignature}: source must be an array`);
|
|
658
|
+
}
|
|
659
|
+
sectionIndicatorFunctions.forEach((fn) => {
|
|
660
|
+
if (typeof fn !== 'function') {
|
|
661
|
+
throw new Error(`${functionSignature}: all section indicators passed must be functions`);
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
const results = source.map((entry) => {
|
|
666
|
+
let isNewSectionTripped = false;
|
|
667
|
+
|
|
668
|
+
sectionIndicatorFunctions.forEach((fn, index) => {
|
|
669
|
+
if (isNewSectionTripped) {
|
|
670
|
+
counters[index] = 0;
|
|
671
|
+
} else {
|
|
672
|
+
isNewSectionTripped = fn(entry) ? true : false;
|
|
673
|
+
|
|
674
|
+
if (isNewSectionTripped) {
|
|
675
|
+
counters[index] += 1;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
if (isNewSectionTripped) {
|
|
681
|
+
subIndex = 0;
|
|
682
|
+
} else {
|
|
683
|
+
subIndex += 1;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return ({ entry, section: [...counters], subIndex });
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
return results;
|
|
690
|
+
};
|
package/src/object.js
CHANGED
|
@@ -16,12 +16,15 @@ const FormatUtils = require('./format');
|
|
|
16
16
|
* * {@link module:object.setPropertyDefaults|setPropertyDefaults()} - sets values for objects that don't currently have the property
|
|
17
17
|
* * {@link module:object.propertyValueSample|propertyValueSample(collection)} - finds non-empty values for all properties found in the collection
|
|
18
18
|
* * Manipulating objects
|
|
19
|
-
* * {@link module:object.
|
|
20
|
-
* * {@link module:object.
|
|
19
|
+
* * {@link module:object.assign|objAssign(object, property, value)} - Applies properties to an object in functional programming style.
|
|
20
|
+
* * {@link module:object.assignEntities|objAssignEntities(object, [property, value])} - Applies properties to an object using Array values - [key,value]
|
|
21
|
+
* * {@link module:object.augment|augment(object, augmentFn)} - Applies properties to an object similar to Map
|
|
22
|
+
* * {@link module:object.augmentInherit|augmentInherit(object, augmentFn)} - Applies properties to a collection of objects, 'remembering' the last value - useful for 1d to *D lists.
|
|
21
23
|
* * {@link module:object.selectObjectProperties|selectObjectProperties()} - keep only specific properties
|
|
22
24
|
* * {@link module:object.filterObjectProperties|filterObjectProperties()} - remove specific properties
|
|
23
25
|
* * {@link module:object.mapProperties|mapProperties(collection, fn, ...properties)} - map multiple properties at once (like parseInt, or toString)
|
|
24
26
|
* * {@link module:object.formatProperties|formatProperties(collection, propertyTranslation)} - map specific properties (ex: toString, toNumber, etc)
|
|
27
|
+
* * {@link module:object.union|union(objectList1, objectList2)} - Unites the properties of two collections of objects.
|
|
25
28
|
* * Fetch child properties from related objects
|
|
26
29
|
* * {@link module:object.fetchObjectProperty|fetchObjectProperty(object, string)} - use dot notation to bring a child property onto a parent
|
|
27
30
|
* * {@link module:object.fetchObjectProperties|fetchObjectProperties(object, string[])} - use dot notation to bring multiple child properties onto a parent
|
|
@@ -1129,3 +1132,186 @@ module.exports.propertyValueSample = function propertyValueSample(objCollection)
|
|
|
1129
1132
|
|
|
1130
1133
|
return result;
|
|
1131
1134
|
};
|
|
1135
|
+
|
|
1136
|
+
/**
|
|
1137
|
+
* Appends values to a collection of objects,
|
|
1138
|
+
* where if the value `undefined` is provided,
|
|
1139
|
+
* then it "remembers" or "inherits" the value previously used.
|
|
1140
|
+
*
|
|
1141
|
+
* This is VERY useful for converting a 1 dimensional list, into a hierarchical tree structure.
|
|
1142
|
+
*
|
|
1143
|
+
* For example, say we got this from a previous successful scrape:
|
|
1144
|
+
*
|
|
1145
|
+
* ```
|
|
1146
|
+
* source = [
|
|
1147
|
+
* { text: '# Overview' },
|
|
1148
|
+
* { text: 'This entire list is a hierarchy of data.' },
|
|
1149
|
+
* { text: '# Section A' },
|
|
1150
|
+
* { text: 'This describes section A' },
|
|
1151
|
+
* { text: '## SubSection 1' },
|
|
1152
|
+
* { text: 'With a subsection belonging to Section A' },
|
|
1153
|
+
* { text: '# Section B' },
|
|
1154
|
+
* { text: 'With an entirely unrelated section B, that is sibling to Section A' }
|
|
1155
|
+
* ];
|
|
1156
|
+
* ```
|
|
1157
|
+
*
|
|
1158
|
+
* We would like to know which heading1 and heading2 the texts belong to:
|
|
1159
|
+
*
|
|
1160
|
+
* ```
|
|
1161
|
+
*
|
|
1162
|
+
* const isHeader1 = (str) => str.startsWith('# ');
|
|
1163
|
+
* const isHeader2 = (str) => str.startsWith('## ');
|
|
1164
|
+
*
|
|
1165
|
+
* //-- note, return undefined for any property you don't want to have inherited.
|
|
1166
|
+
* inheritFn = (entry) => ({
|
|
1167
|
+
* section: isHeader1(entry.text) ? entry.text.replace(/#+\s+/, '') : undefined,
|
|
1168
|
+
* subSection: isHeader2(entry.text) ? entry.text.replace(/#+\s+/, '') : undefined
|
|
1169
|
+
* });
|
|
1170
|
+
*
|
|
1171
|
+
* results = utils.object.augmentInherit(source, inheritFn);
|
|
1172
|
+
* ```
|
|
1173
|
+
*
|
|
1174
|
+
* text |section |subSection
|
|
1175
|
+
* -- |-- |--
|
|
1176
|
+
* Overview |Overview |undefined
|
|
1177
|
+
* This entire list is a hierarchy of data. |Overview |undefined
|
|
1178
|
+
* Section A |Section A|undefined
|
|
1179
|
+
* This describes section A |Section A|undefined
|
|
1180
|
+
* SubSection 1 |Section A|SubSection 1
|
|
1181
|
+
* With a subsection belonging to Section A |Section A|SubSection 1
|
|
1182
|
+
* Section B |Section B|undefined
|
|
1183
|
+
* With an entirely unrelated section B, that is sibling to Section A|Section B|undefined
|
|
1184
|
+
* SubSection 1 |Section B|SubSection 1
|
|
1185
|
+
* And another subsection 1, but this time related to Section B. |Section B|SubSection 1
|
|
1186
|
+
*
|
|
1187
|
+
* So we pass the collection of results as the source, and an augment function,
|
|
1188
|
+
* that returns the heading 1 value - that is then kept until the next heading 1.
|
|
1189
|
+
* (Similar for subSection using heading 2)
|
|
1190
|
+
*
|
|
1191
|
+
* @param {Object[]} source - the collection of objects to check and augment.
|
|
1192
|
+
* @param {Function} augmentFn - function accepting each entry, and returning the properties to "inherit" <br /> or a property with a value of undefined - if it should not be preserved.
|
|
1193
|
+
* @returns {Object[]} - new version of the source objects with the properties applied.
|
|
1194
|
+
*
|
|
1195
|
+
* @see {@link module:object.augment|augment()} - Applies properties to an object similar to Map
|
|
1196
|
+
*/
|
|
1197
|
+
module.exports.augmentInherit = function augmentInherit(source, augmentFn) {
|
|
1198
|
+
const signature = 'augmentInherit(source, augmentFn)';
|
|
1199
|
+
if (!Array.isArray(source)) {
|
|
1200
|
+
throw new Error(`${signature}: source must be an array`);
|
|
1201
|
+
} else if (typeof augmentFn !== 'function') {
|
|
1202
|
+
throw new Error(`${signature}: augmentFn must be a function of signature: (entry, lastValue) => obj`);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
let keys;
|
|
1206
|
+
|
|
1207
|
+
let lastValue = {};
|
|
1208
|
+
return source.map((entry, index) => {
|
|
1209
|
+
const fnResult = augmentFn(entry, lastValue);
|
|
1210
|
+
|
|
1211
|
+
//-- ignore all values that are undefined
|
|
1212
|
+
const newValue = { ...lastValue };
|
|
1213
|
+
let isFlipped = false;
|
|
1214
|
+
keys = Object.keys(fnResult || {});
|
|
1215
|
+
keys.forEach((key) => {
|
|
1216
|
+
if (isFlipped) {
|
|
1217
|
+
newValue[key] = undefined;
|
|
1218
|
+
} else if (fnResult[key] !== undefined) {
|
|
1219
|
+
newValue[key] = fnResult[key];
|
|
1220
|
+
isFlipped = true;
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
// console.log(`index:${index}: entry:${JSON.stringify(entry)}, newValue:${JSON.stringify(newValue)}, lastValue:${JSON.stringify(lastValue)}`)
|
|
1225
|
+
const result = ({ ...entry, ...newValue });
|
|
1226
|
+
lastValue = newValue;
|
|
1227
|
+
return result;
|
|
1228
|
+
});
|
|
1229
|
+
};
|
|
1230
|
+
|
|
1231
|
+
/**
|
|
1232
|
+
* Unites the properties of two collections of objects.
|
|
1233
|
+
*
|
|
1234
|
+
* For example:
|
|
1235
|
+
*
|
|
1236
|
+
* ```
|
|
1237
|
+
* source1 = [
|
|
1238
|
+
* { first: 'john' },
|
|
1239
|
+
* { first: 'jane' }
|
|
1240
|
+
* ];
|
|
1241
|
+
* source2 = [
|
|
1242
|
+
* { last: 'doe' },
|
|
1243
|
+
* { last: 'dough' }
|
|
1244
|
+
* ];
|
|
1245
|
+
* utils.object.union(source1, source2);
|
|
1246
|
+
* // [{ first: 'john', last: 'doe' },
|
|
1247
|
+
* // { first: 'jane', last: 'dough' }];
|
|
1248
|
+
* ```
|
|
1249
|
+
*
|
|
1250
|
+
* Note that you can also pass a single object, to have it union to multiple.
|
|
1251
|
+
*
|
|
1252
|
+
* ```
|
|
1253
|
+
* source1 = [
|
|
1254
|
+
* { first: 'john' },
|
|
1255
|
+
* { first: 'jane' }
|
|
1256
|
+
* ];
|
|
1257
|
+
* //-- same object to be applied to all
|
|
1258
|
+
* source2 = { last: 'doe' };
|
|
1259
|
+
*
|
|
1260
|
+
* utils.object.union(source1, source2);
|
|
1261
|
+
* // [{ first: 'john', last: 'doe' },
|
|
1262
|
+
* // { first: 'jane', last: 'doe' }];
|
|
1263
|
+
* ```
|
|
1264
|
+
*
|
|
1265
|
+
* @param {Object[]|Object} source1 - object or array of objects to union
|
|
1266
|
+
* @param {Object[]|Object} source2 - object or array of objects to union
|
|
1267
|
+
* @returns {Object[]} - collection of objects merging the values between the two sources
|
|
1268
|
+
*
|
|
1269
|
+
* @see {@link module:object.join|join} - to instead join based on a value instead of index
|
|
1270
|
+
* @see {@link module:object.filterObjectProperties|filterObjectProperties} - to remove properties from collection of objects.
|
|
1271
|
+
*/
|
|
1272
|
+
module.exports.union = function union(source1, source2) {
|
|
1273
|
+
const signature = 'union(source1:object[], source2:object[])';
|
|
1274
|
+
|
|
1275
|
+
let s1Iterator;
|
|
1276
|
+
let s1Entry;
|
|
1277
|
+
let s1Length;
|
|
1278
|
+
let s2Iterator;
|
|
1279
|
+
let s2Entry;
|
|
1280
|
+
let s2Length;
|
|
1281
|
+
|
|
1282
|
+
if (Array.isArray(source1)) {
|
|
1283
|
+
s1Iterator = source1.entries();
|
|
1284
|
+
s1Length = source1.length;
|
|
1285
|
+
} else if (typeof source1 === 'object') {
|
|
1286
|
+
s1Iterator = ({ next: () => ({ done: false, value: [0, source1] }) });
|
|
1287
|
+
s1Length = 1;
|
|
1288
|
+
} else {
|
|
1289
|
+
throw new Error(`${signature}: source1 must be a collection of objects, or a single object`);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
if (Array.isArray(source2)) {
|
|
1293
|
+
s2Iterator = source2.entries();
|
|
1294
|
+
s2Length = source2.length;
|
|
1295
|
+
} else if (typeof source2 === 'object') {
|
|
1296
|
+
s2Iterator = ({ next: () => ({ done: false, value: [0, source2] }) });
|
|
1297
|
+
s2Length = 1;
|
|
1298
|
+
} else {
|
|
1299
|
+
throw new Error(`${signature}: source2 must be a collection of objects, or a single object`);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
const len = Math.max(s1Length, s2Length);
|
|
1303
|
+
const results = new Array(len);
|
|
1304
|
+
|
|
1305
|
+
for (let i = 0; i < len; i += 1) {
|
|
1306
|
+
s1Entry = s1Iterator.next();
|
|
1307
|
+
s1Entry = s1Entry.done ? {} : s1Entry.value[1];
|
|
1308
|
+
|
|
1309
|
+
s2Entry = s2Iterator.next();
|
|
1310
|
+
s2Entry = s2Entry.done ? {} : s2Entry.value[1];
|
|
1311
|
+
|
|
1312
|
+
//console.log(`s1Entry: ${JSON.stringify(s1Entry)}, s2Entry: ${JSON.stringify(s2Entry)}`);
|
|
1313
|
+
results[i] = { ...s1Entry, ...s2Entry };
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
return results;
|
|
1317
|
+
};
|