jupyter-ijavascript-utils 1.55.0 → 1.57.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 +6 -0
- package/Dockerfile +1 -1
- package/README.md +6 -0
- package/package.json +1 -1
- package/src/array.js +15 -8
- package/src/date.js +135 -42
- package/src/file.js +105 -14
- package/src/hashMap.js +3 -4
- package/src/ijs.js +103 -0
- package/src/object.js +75 -4
package/DOCS.md
CHANGED
|
@@ -74,6 +74,12 @@ Give it a try here:
|
|
|
74
74
|
[](https://mybinder.org/v2/gh/paulroth3d/jupyter-ijavascript-utils/main?labpath=example.ipynb)
|
|
75
75
|
|
|
76
76
|
## What's New
|
|
77
|
+
* 1.57 - #86 - include examples on binning based on time series {@link https://github.com/paulroth3d/jupyter-ijavascript-utils/issues/86|see more here}
|
|
78
|
+
* #87 - add in {@link module:file.fileExists|file.fileExists} to make things easier for new people getting started
|
|
79
|
+
* #89 - allow {@link module:array.resize|array.resize} to work with defaults, if zipping arrays of different sizes
|
|
80
|
+
* #90 - correct issue with timezone offsets, so it is no longer possible to get a timezone offset +24:00 from any of the localizations - like {@link module:date.toLocalISO|date.toLocalISO}
|
|
81
|
+
* #92 - make it easier to handle big expensive calculations with a cache, see {@link module:ijs.useCache|ijs.useCache()} and {@link module:file.useCache|file.useCache()}
|
|
82
|
+
* 1.56 - #84 (object.renamePropertiesFromList), #82 (date.getWeekday)
|
|
77
83
|
* 1.55 - #76, #77, #74, #78
|
|
78
84
|
* 1.54 - additional Date logic, and formatting. #70 #71 #72
|
|
79
85
|
* 1.53 - additional docs and examples for {@link module:color|color/colour} package.
|
package/Dockerfile
CHANGED
package/README.md
CHANGED
|
@@ -54,6 +54,12 @@ This is not intended to be the only way to accomplish many of these tasks, and a
|
|
|
54
54
|

|
|
55
55
|
|
|
56
56
|
# What's New
|
|
57
|
+
* 1.57 - #86 - include examples on binning based on time series
|
|
58
|
+
* #87 - add in fileExists to make things easier for new people getting started
|
|
59
|
+
* #89 - allow resizing arrays with defaults, if zipping arrays of different sizes
|
|
60
|
+
* #90 - correct issue with timezone offsets, so it is no longer possible to get a timezone offset +24:00
|
|
61
|
+
* #92 - make it easier to handle big expensive calculations with a cache
|
|
62
|
+
* 1.56 - #84 (object.renamePropertiesFromList), #82 (date.getWeekday)
|
|
57
63
|
* 1.55 - #76, #77, #74, #78
|
|
58
64
|
* 1.54 - additional Date logic, and formatting. #70 #71 #72
|
|
59
65
|
* 1.53 - additional docs and examples for color/colour package.
|
package/package.json
CHANGED
package/src/array.js
CHANGED
|
@@ -1460,18 +1460,25 @@ module.exports.asyncWaitAndChain = (seconds, fn, rows) => {
|
|
|
1460
1460
|
*
|
|
1461
1461
|
* utils.array.resize(categoryValues, 2); // ['rock', 'paper']
|
|
1462
1462
|
* utils.array.resize(categoryValues, 7); // ['rock', 'paper', 'scissors',
|
|
1463
|
-
*
|
|
1463
|
+
* undefined, undefined, undefined, undefined];
|
|
1464
1464
|
* ```
|
|
1465
1465
|
*
|
|
1466
1466
|
* @param {Array} sourceList - array of values
|
|
1467
|
-
* @param {Number} length - new
|
|
1467
|
+
* @param {Number} length - new length of the list
|
|
1468
1468
|
*/
|
|
1469
|
-
module.exports.resize = function resize(sourceList, length) {
|
|
1469
|
+
module.exports.resize = function resize(sourceList, length, defaultValue = undefined) {
|
|
1470
1470
|
if (!sourceList || !Array.isArray(sourceList)) return [];
|
|
1471
1471
|
if (length < 1 || sourceList.length < 1) return [];
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
.
|
|
1472
|
+
|
|
1473
|
+
const result = new Array(length)
|
|
1474
|
+
.fill(defaultValue);
|
|
1475
|
+
|
|
1476
|
+
sourceList.forEach((val, index) => {
|
|
1477
|
+
if (index >= length) return;
|
|
1478
|
+
result[index] = val;
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1481
|
+
return result;
|
|
1475
1482
|
};
|
|
1476
1483
|
|
|
1477
1484
|
/**
|
|
@@ -1532,9 +1539,9 @@ module.exports.zip = function zip(arrayLeft, arrayRight, ...rest) {
|
|
|
1532
1539
|
if (cleanLeft.length === 0 && cleanRight.length === 0) {
|
|
1533
1540
|
result = [[]];
|
|
1534
1541
|
} else if (cleanLeft.length === 0) {
|
|
1535
|
-
result = cleanRight.map((val) => Array.isArray(val) ? val : [val]);
|
|
1542
|
+
result = cleanRight.map((val) => Array.isArray(val) ? val : (typeof val === 'string') ? [val] : [...val]);
|
|
1536
1543
|
} else if (cleanRight.length === 0) {
|
|
1537
|
-
result = cleanLeft.map((val) => Array.isArray(val) ? val : [val]);
|
|
1544
|
+
result = cleanLeft.map((val) => Array.isArray(val) ? val : (typeof val === 'string') ? [val] : [...val]);
|
|
1538
1545
|
} else {
|
|
1539
1546
|
const cleanLeftLen = cleanLeft.length;
|
|
1540
1547
|
const cleanRightLen = cleanRight.length;
|
package/src/date.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* * {@link module:date.parse|date.parse(String)} - parse a date and throw an exception if it is not a valid date
|
|
9
9
|
* * Timezones
|
|
10
10
|
* * {@link module:date.toLocalISO|date.toLocalISO} - prints in 8601 format with timezone offset based on a tz entry - like america/chicago
|
|
11
|
+
* * {@link module:date.localISOFormatter|date.localISOFormatter} - prints in 8601 format - slightly improved performance for large scale use
|
|
11
12
|
* * {@link module:date.getTimezoneOffset|date.getTimezoneOffset(String)} - gets the number of milliseconds offset for a given timezone
|
|
12
13
|
* * {@link module:date.correctForTimezone|date.correctForTimezone(Date, String)} - meant to correct a date already off from UTC to the correct time
|
|
13
14
|
* * {@link module:date.epochShift|date.epochShift(Date, String)} - offsets a date from UTC to a given time amount
|
|
@@ -180,12 +181,22 @@ module.exports.durationLong = function durationLong(epochDifference) {
|
|
|
180
181
|
return `${signStr}${days} days, ${hours} hours, ${minutes} minutes, ${seconds}.${milli} seconds`;
|
|
181
182
|
};
|
|
182
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Function that is passed a date for formatting
|
|
186
|
+
*
|
|
187
|
+
* @callback dateFormatter
|
|
188
|
+
* @param {Date} dateToFormat - the date to format
|
|
189
|
+
*/
|
|
190
|
+
|
|
183
191
|
/**
|
|
184
192
|
* @typedef {Object} TimezoneEntry
|
|
185
193
|
* @property {String} tz - the name of the timezone
|
|
186
194
|
* @property {Function} formatter - formats a date to that local timezone
|
|
187
195
|
* @property {Number} epoch - the difference in milliseconds from that tz to UTC
|
|
188
196
|
* @property {String} offset - ISO format for how many hours and minutes offset to UTC '+|-' HH:MMM
|
|
197
|
+
* @property {dateFormatter} toLocalISO - formatter function that formats a date to local ISO
|
|
198
|
+
* @property {dateFormatter} toLocalISOWeekday - formatter function that formats a date to local ISO + weekday
|
|
199
|
+
* @property {dateFormatter} getWeekday - formatter function that determines the day of week for a date
|
|
189
200
|
*/
|
|
190
201
|
|
|
191
202
|
/**
|
|
@@ -220,6 +231,12 @@ module.exports.getTimezoneEntry = function getTimezoneEntry(timezoneStr) {
|
|
|
220
231
|
timeZone: cleanTz
|
|
221
232
|
});
|
|
222
233
|
|
|
234
|
+
const dayOfWeekFormat = new Intl.DateTimeFormat('en-us', {
|
|
235
|
+
weekday: 'short',
|
|
236
|
+
timeZone: cleanTz
|
|
237
|
+
});
|
|
238
|
+
const getWeekday = (date) => dayOfWeekFormat.format(date);
|
|
239
|
+
|
|
223
240
|
const getOffset = (dateValue) => {
|
|
224
241
|
const dm = dtFormat.formatToParts(dateValue)
|
|
225
242
|
.filter(({ type }) => type !== 'literal')
|
|
@@ -249,7 +266,8 @@ module.exports.getTimezoneEntry = function getTimezoneEntry(timezoneStr) {
|
|
|
249
266
|
|
|
250
267
|
const impactedDate = getOffset(d);
|
|
251
268
|
|
|
252
|
-
|
|
269
|
+
//-- avoid +24:00 or -24:00 - the number of seconds within a day
|
|
270
|
+
const diff = (d.getTime() - impactedDate.getTime()) % 86400000;
|
|
253
271
|
|
|
254
272
|
const diffSign = diff > 0 ? '-' : '+';
|
|
255
273
|
let remainder = DateUtils.divideRemainder(Math.abs(diff), DateUtils.TIME.HOUR);
|
|
@@ -258,7 +276,10 @@ module.exports.getTimezoneEntry = function getTimezoneEntry(timezoneStr) {
|
|
|
258
276
|
const diffMinutes = remainder.value;
|
|
259
277
|
const offset = `${diffSign}${DateUtils.padTime(diffHours)}:${DateUtils.padTime(diffMinutes)}`;
|
|
260
278
|
|
|
261
|
-
const
|
|
279
|
+
const toLocalISO = (date) => `${formatter(date)}${offset}`;
|
|
280
|
+
const toLocalISOWeekday = (date) => `${formatter(date)}${offset} - ${getWeekday(date)}`;
|
|
281
|
+
|
|
282
|
+
const result = ({ tz: cleanTz, formatter, toLocalISO, toLocalISOWeekday, getWeekday, epoch: diff, offset });
|
|
262
283
|
|
|
263
284
|
DateUtils.timezoneOffsetMap.set(cleanTz, result);
|
|
264
285
|
|
|
@@ -381,27 +402,14 @@ module.exports.correctForOtherTimezone = function correctForTimezones(date, sour
|
|
|
381
402
|
*
|
|
382
403
|
* Use this if you somehow have a date that needs to be shifted by the timezone offset.
|
|
383
404
|
*
|
|
384
|
-
*
|
|
385
|
-
* you can use this to correct for "local dates" but are in another timezone than you are in.
|
|
386
|
-
*
|
|
387
|
-
* (For example, you got a local date for 2:15 PM EST, but your current computer is in CST)
|
|
405
|
+
* For example, if you have a time that is already in GMT, and want the date shifted by a timezone.
|
|
388
406
|
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
* * Parse the date assuming local timezone
|
|
392
|
-
* * Parse the date using [ISO 8601 formats](https://www.iso.org/iso-8601-date-and-time-format.html)
|
|
393
|
-
* * This option DOES provide an option for providing a timezone offset (ex: `-0500`)
|
|
394
|
-
*
|
|
395
|
-
* Since this is the opposite of {@link module:date.correctForTimezone|date.correctForTimezone}, this can be useful.
|
|
396
|
-
*
|
|
397
|
-
* But most likely, you'd like to use either:
|
|
398
|
-
*
|
|
399
|
-
* * {@link module:date.correctForTimezone|date.correctForTimezone} or
|
|
400
|
-
* * {@link module:date.correctForTimezones|date.correctForTimezones}.
|
|
407
|
+
* This is used internally for {@link module:date.correctForOtherTimezone|date.correctForOtherTimezone}
|
|
408
|
+
* if local dates are provided - but for a different timezone you yourself are not in.
|
|
401
409
|
*
|
|
402
410
|
* ---
|
|
403
411
|
*
|
|
404
|
-
* Epoch shift a date, so the utcDate is no longer correct,
|
|
412
|
+
* Epoch shift changes the internals of a JavaScript date, so the utcDate is no longer correct,
|
|
405
413
|
* but many other functions behave closer to expected.
|
|
406
414
|
*
|
|
407
415
|
* Once you epoch shift the date, then time stored in the date is incorrect (because it always points to GMT)
|
|
@@ -410,20 +418,8 @@ module.exports.correctForOtherTimezone = function correctForTimezones(date, sour
|
|
|
410
418
|
*
|
|
411
419
|
* See {@link https://stackoverflow.com/a/15171030|here why this might not be what you want}
|
|
412
420
|
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
* Sometimes though, some libraries use the "getMonth()", "getDate()" of the date, and do not support using timezones.
|
|
416
|
-
*
|
|
417
|
-
* That is when this shines.
|
|
418
|
-
*
|
|
419
|
-
* ```
|
|
420
|
-
* timeStamp = 1738437341000;
|
|
421
|
-
* d = new Date(timeStamp);
|
|
422
|
-
* d.toIsoString(); // 2025-02-01T19:15:41.000Z
|
|
423
|
-
* `current time is: ${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}` // 'current time is: 2025-2-1'
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
* ```
|
|
421
|
+
* * {@link module:date.correctForTimezone|date.correctForTimezone} or
|
|
422
|
+
* * {@link module:date.correctForTimezones|date.correctForTimezones}.
|
|
427
423
|
*
|
|
428
424
|
* @param {Date} date - date to shift
|
|
429
425
|
* @param {String} timezoneStr - the tz database name of the timezone
|
|
@@ -456,13 +452,66 @@ module.exports.epochShift = function epochShift(date, timezoneStr) {
|
|
|
456
452
|
* utils.date.toLocalISO(d, 'europe/paris'); // '2024-12-27T14:30:00.000+01:00'
|
|
457
453
|
* ```
|
|
458
454
|
*
|
|
455
|
+
* Sometimes it is helpful to have the weekday to make sense of things
|
|
456
|
+
*
|
|
457
|
+
* ```
|
|
458
|
+
* utils.date.toLocalISO(d, 'america/Chicago', true); // '2024-12-27T07:30:00.000-06:00 FRI'
|
|
459
|
+
* utils.date.toLocalISO(d, 'europe/paris', true); // '2024-12-27T14:30:00.000+01:00 FRI'
|
|
460
|
+
* ```
|
|
461
|
+
*
|
|
459
462
|
* @param {Date} date - date to print
|
|
460
463
|
* @param {String} timezoneStr - the tz database name of the timezone
|
|
464
|
+
* @param {Boolean} [includeWeekday=false] - whether to include the weekday
|
|
465
|
+
* @see {@link module:date.localISOFormatter|date.localISOFormatter} - if you're converting to string frequently
|
|
461
466
|
* @returns {String} - ISO format with timezone offset
|
|
462
467
|
*/
|
|
463
|
-
module.exports.toLocalISO = function toLocalISO(date, timezoneStr) {
|
|
464
|
-
|
|
465
|
-
|
|
468
|
+
module.exports.toLocalISO = function toLocalISO(date, timezoneStr, includeWeekday = false) {
|
|
469
|
+
if (includeWeekday) {
|
|
470
|
+
return DateUtils.getTimezoneEntry(timezoneStr).toLocalISOWeekday(date);
|
|
471
|
+
}
|
|
472
|
+
return DateUtils.getTimezoneEntry(timezoneStr).toLocalISO(date);
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* If repeatedly asking for a local time, use this method instead.
|
|
477
|
+
*
|
|
478
|
+
* ```
|
|
479
|
+
* myDate = new Date('2025-01-15T06:00:00.000Z');
|
|
480
|
+
* centralFormatter = utils.date.localISOFormatter('us/central');
|
|
481
|
+
* centralFormatter(myDate); // '2025-01-15T00:00:00.000Z'
|
|
482
|
+
* ```
|
|
483
|
+
*
|
|
484
|
+
* as opposed to
|
|
485
|
+
*
|
|
486
|
+
* ```
|
|
487
|
+
* myDate = new Date('2025-01-15T06:00:00.000Z');
|
|
488
|
+
* utils.date.toLocalISO(myDate, 'us/central'); // '2025-01-15T00:00:00.000Z'
|
|
489
|
+
* ```
|
|
490
|
+
*
|
|
491
|
+
* @param {String} timezoneStr
|
|
492
|
+
* @returns {dateFormatter} - (date) => {String} 'yyyy-mm-ddThh:mm:ss.MMM[+-]TZOFFSET'
|
|
493
|
+
* @see {@link module:date.toLocalISO|date.toLocalISO}
|
|
494
|
+
*/
|
|
495
|
+
module.exports.localISOFormatter = function localISOFormatter(timezoneStr, includeWeekday = false) {
|
|
496
|
+
if (includeWeekday) {
|
|
497
|
+
return DateUtils.getTimezoneEntry(timezoneStr).toLocalISOWeekday;
|
|
498
|
+
}
|
|
499
|
+
return DateUtils.getTimezoneEntry(timezoneStr).toLocalISO;
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Determines the weekday of a date
|
|
504
|
+
* @param {Date} date - date to print
|
|
505
|
+
* @param {String} timezoneStr - the tz database name of the timezone
|
|
506
|
+
* @returns {String} - Currently returns `en-us` formatted day of week of a date
|
|
507
|
+
* @see {@link module:date.toLocalISO|date.toLocalISO}
|
|
508
|
+
* @example
|
|
509
|
+
* date = new Date('2025-01-15T06:00:00.000Z');
|
|
510
|
+
* utils.date.getWeekday(date, 'us/pacific'); // Tue
|
|
511
|
+
* utils.date.getWeekday(date, 'us/eastern'); // Wed
|
|
512
|
+
*/
|
|
513
|
+
module.exports.getWeekday = function weekdayFormatter(date, timezoneStr) {
|
|
514
|
+
return DateUtils.getTimezoneEntry(timezoneStr).getWeekday(date);
|
|
466
515
|
};
|
|
467
516
|
|
|
468
517
|
module.exports.toIsoStringNoTimezone = function toIsoStringNoTimezone(date) {
|
|
@@ -771,12 +820,19 @@ class DateRange {
|
|
|
771
820
|
*/
|
|
772
821
|
endDate;
|
|
773
822
|
|
|
823
|
+
/**
|
|
824
|
+
* Data attached to the DateTime
|
|
825
|
+
* @type {any}
|
|
826
|
+
*/
|
|
827
|
+
data;
|
|
828
|
+
|
|
774
829
|
/**
|
|
775
830
|
* @param {Date|String} startDate - the starting date
|
|
776
831
|
* @param {Date|String} endDate - the ending date
|
|
832
|
+
* @param {any} [data] - any data to store
|
|
777
833
|
*/
|
|
778
|
-
constructor(startDate, endDate) {
|
|
779
|
-
this.reinitialize(startDate, endDate);
|
|
834
|
+
constructor(startDate, endDate, data = null) {
|
|
835
|
+
this.reinitialize(startDate, endDate, data);
|
|
780
836
|
}
|
|
781
837
|
|
|
782
838
|
/**
|
|
@@ -794,6 +850,31 @@ class DateRange {
|
|
|
794
850
|
* // {start: 2025-03-01TT00:00:00, end: 2025-04-01TT00:00:00}]
|
|
795
851
|
* ```
|
|
796
852
|
*
|
|
853
|
+
* Often though, we want to remember something about the DateRange,
|
|
854
|
+
* like which dates that it collected.
|
|
855
|
+
*
|
|
856
|
+
* ```
|
|
857
|
+
* arrayGenerator = function() { return [] };
|
|
858
|
+
* rangeList = utils.DateRange.fromList(dates, arrayGenerator);
|
|
859
|
+
* // [{start: 2025-01-01T00:00:00, end: 2025-02-01TT00:00:00},
|
|
860
|
+
* // {start: 2025-02-01TT00:00:00, end: 2025-03-01TT00:00:00},
|
|
861
|
+
* // {start: 2025-03-01TT00:00:00, end: 2025-04-01TT00:00:00}]
|
|
862
|
+
*
|
|
863
|
+
* dates.forEach((date) => rangeList
|
|
864
|
+
* .find(rl => rl.contains(date))
|
|
865
|
+
* .data.push(date)
|
|
866
|
+
* );
|
|
867
|
+
*
|
|
868
|
+
* rangeList
|
|
869
|
+
* .map(rl => `${rl.toString()}: has ${rl.data.length}`)
|
|
870
|
+
* .join('\n');
|
|
871
|
+
*
|
|
872
|
+
* // 2025-01-01T00:00:00.000Z to 2025-02-01T00:00:00.000Z: has 2
|
|
873
|
+
* // 2025-02-01T00:00:00.000Z to 2025-03-01T00:00:00.000Z: has 1
|
|
874
|
+
* // 2025-03-01T00:00:00.000Z to 2025-04-01T00:00:00.000Z: has 1
|
|
875
|
+
*
|
|
876
|
+
* ```
|
|
877
|
+
*
|
|
797
878
|
* (Note: you can also use {@link module:date.arrange|date.arrange} or
|
|
798
879
|
* {@link module:date.generateDateSequence|date.generateDateSequence}
|
|
799
880
|
* to come up with the list of those dates)
|
|
@@ -802,18 +883,27 @@ class DateRange {
|
|
|
802
883
|
* the simplest is to remove the dates from the resulting list.)
|
|
803
884
|
*
|
|
804
885
|
* @param {Date[]} dateList - list of dates
|
|
886
|
+
* @param {Function} [dataCreationFn] - optional generator for data to be stored in each DataRange in the sequence
|
|
805
887
|
* @returns {DateRange[]} - list of dateList.length-1 dateRanges,
|
|
806
888
|
* where the end of the firstRange is the start of the next.
|
|
807
889
|
* @see {@link module:date.arrange|date.arrange} - to create dates by adding a value multiple times
|
|
808
890
|
* @see {@link module:date.generateDateSequence|date.generateDateSequence} - to create dates between a start and an end date
|
|
809
891
|
*/
|
|
810
|
-
static fromList(dateSequence) {
|
|
892
|
+
static fromList(dateSequence, dataCreationFn) {
|
|
811
893
|
if (dateSequence.length < 2) return [];
|
|
812
894
|
|
|
813
895
|
const results = new Array(dateSequence.length - 2);
|
|
814
|
-
|
|
815
|
-
|
|
896
|
+
|
|
897
|
+
if (dataCreationFn) {
|
|
898
|
+
for (let i = 0; i < dateSequence.length - 1; i += 1) {
|
|
899
|
+
results[i] = new DateRange(dateSequence[i], dateSequence[i + 1], dataCreationFn());
|
|
900
|
+
}
|
|
901
|
+
} else {
|
|
902
|
+
for (let i = 0; i < dateSequence.length - 1; i += 1) {
|
|
903
|
+
results[i] = new DateRange(dateSequence[i], dateSequence[i + 1]);
|
|
904
|
+
}
|
|
816
905
|
}
|
|
906
|
+
|
|
817
907
|
return results;
|
|
818
908
|
}
|
|
819
909
|
|
|
@@ -824,8 +914,9 @@ class DateRange {
|
|
|
824
914
|
*
|
|
825
915
|
* @param {Date|String} startDate - the starting date
|
|
826
916
|
* @param {Date|String} endDate - the ending date
|
|
917
|
+
* @param {any} [data] - any data to store
|
|
827
918
|
*/
|
|
828
|
-
reinitialize(startDate, endDate) {
|
|
919
|
+
reinitialize(startDate, endDate, data = null) {
|
|
829
920
|
const cleanStart = startDate instanceof Date
|
|
830
921
|
? startDate
|
|
831
922
|
: new Date(Date.parse(startDate));
|
|
@@ -840,6 +931,8 @@ class DateRange {
|
|
|
840
931
|
this.startDate = cleanStart;
|
|
841
932
|
this.endDate = cleanEnd;
|
|
842
933
|
}
|
|
934
|
+
|
|
935
|
+
this.data = data;
|
|
843
936
|
}
|
|
844
937
|
|
|
845
938
|
/**
|
package/src/file.js
CHANGED
|
@@ -29,6 +29,9 @@ const logger = require('./logger');
|
|
|
29
29
|
* * {@link module:file.matchFiles|matchFiles(path, matchingFn)} - find files or directories based on type of file or name
|
|
30
30
|
* * checking files exist
|
|
31
31
|
* * {@link module:file.checkFile|checkFile(...paths)} - check if a file at a path exists
|
|
32
|
+
* * {@link module:file.fileExists|fileExists(filePath)} - check if a single file at a path exists
|
|
33
|
+
* * using a cache for long running executions
|
|
34
|
+
* * {@link module:file.useCache|file.useCache()} - perform an expensive calculation and write to a cache, or read from the cache transparently
|
|
32
35
|
*
|
|
33
36
|
* ---
|
|
34
37
|
*
|
|
@@ -96,6 +99,8 @@ const FileUtil = module.exports;
|
|
|
96
99
|
*
|
|
97
100
|
* @param {string} filePath - path of the file to load
|
|
98
101
|
* @param {Object} fsOptions - options to pass for fsRead (ex: { encoding: 'utf-8' })
|
|
102
|
+
* @param {Function} fsOptions.formatter - formatter to use when writing the JSON
|
|
103
|
+
* @param {String} fsOptions.encoding - the encoding to write the JSON out with
|
|
99
104
|
* @example
|
|
100
105
|
* const weather = [
|
|
101
106
|
* { id: 1, city: 'Seattle', month: 'Aug', precip: 0.87 },
|
|
@@ -117,7 +122,11 @@ const FileUtil = module.exports;
|
|
|
117
122
|
module.exports.readJSON = function readJSON(filePath, fsOptions = {}) {
|
|
118
123
|
const resolvedPath = path.resolve(filePath);
|
|
119
124
|
const optionsDefaults = { encoding: 'utf-8' };
|
|
120
|
-
|
|
125
|
+
let cleanedOptions = { ...optionsDefaults, ...fsOptions };
|
|
126
|
+
|
|
127
|
+
//-- unfortunately we cannot pass the formatter in addition, it must replace
|
|
128
|
+
//-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
|
|
129
|
+
if (cleanedOptions.formatter) cleanedOptions = cleanedOptions.formatter;
|
|
121
130
|
|
|
122
131
|
/** @type {string} */
|
|
123
132
|
let result;
|
|
@@ -227,12 +236,14 @@ module.exports.readFile = function readFile(filePath, fsOptions = {}) {
|
|
|
227
236
|
*/
|
|
228
237
|
module.exports.writeJSON = function writeJSON(filePath, contents, fsOptions = {}) {
|
|
229
238
|
//-- if it isn't desired, simply pass as a string.
|
|
230
|
-
const jsonContents = JSON.stringify(contents, null, 2);
|
|
231
239
|
const optionsDefaults = { encoding: 'utf-8' };
|
|
232
240
|
const cleanedOptions = { ...optionsDefaults, ...fsOptions };
|
|
233
241
|
const isAppend = cleanedOptions.append === true;
|
|
234
242
|
const prefix = cleanedOptions.prefix || '';
|
|
235
243
|
const suffix = cleanedOptions.suffix || '';
|
|
244
|
+
const formatter = cleanedOptions.formatter || null;
|
|
245
|
+
const spacing = cleanedOptions.spacing || 2;
|
|
246
|
+
const jsonContents = JSON.stringify(contents, formatter, spacing);
|
|
236
247
|
|
|
237
248
|
// const resolvedPath = path.resolve(filePath);
|
|
238
249
|
try {
|
|
@@ -465,23 +476,103 @@ module.exports.checkFile = function checkFile(...files) {
|
|
|
465
476
|
return notFoundFiles;
|
|
466
477
|
};
|
|
467
478
|
|
|
468
|
-
|
|
469
|
-
*
|
|
470
|
-
* @param {String
|
|
471
|
-
* @
|
|
479
|
+
/**
|
|
480
|
+
* Checks if a single file exists
|
|
481
|
+
* @param {String} filePath - path to check if the file exists.
|
|
482
|
+
* @returns {Boolean} - if the file exists (true) or not (false)
|
|
483
|
+
* @see {@link module:file.checkFile|file.checkFile} - if checking multiple files
|
|
472
484
|
*/
|
|
485
|
+
module.exports.fileExists = function fileExists(filePath) {
|
|
486
|
+
const resolvedPath = path.resolve(filePath);
|
|
487
|
+
return fs.existsSync(resolvedPath);
|
|
488
|
+
};
|
|
489
|
+
|
|
473
490
|
/*
|
|
474
|
-
|
|
475
|
-
|
|
491
|
+
//-- not needed - dates already serialize to iso Strings
|
|
492
|
+
module.exports.cacheSerializer = (key, value) => {
|
|
493
|
+
if (key && (key === 'date' || key.endsWith('_date')) && (value instanceof Date)) {
|
|
494
|
+
return value.toISOString();
|
|
495
|
+
}
|
|
496
|
+
return value;
|
|
497
|
+
};
|
|
498
|
+
*/
|
|
476
499
|
|
|
477
|
-
|
|
500
|
+
module.exports.cacheDeserializer = (key, value) => {
|
|
501
|
+
if (key && (key === 'date' || key.endsWith('_date'))) {
|
|
502
|
+
return new Date(value);
|
|
503
|
+
}
|
|
504
|
+
return value;
|
|
505
|
+
};
|
|
478
506
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
507
|
+
/**
|
|
508
|
+
* For very long or time-intensive executions, sometimes it is better to cache the results
|
|
509
|
+
* than to execute them every single time.
|
|
510
|
+
*
|
|
511
|
+
* Note that this works synchronously, and can be easier to use than if promises are involved.
|
|
512
|
+
*
|
|
513
|
+
* As opposed to {@link module:ijs.useCache|ijs.useCache} - which works with promises.
|
|
514
|
+
*
|
|
515
|
+
* ```
|
|
516
|
+
* shouldWrite = true; /// we will write to the cache with the results from the execution
|
|
517
|
+
* expensiveResults = utils.file.useCache(shouldWrite, './cache', 'expensive.json', () => {
|
|
518
|
+
* const data = d3.csvParse(utils.file.readFile('./someFile.csv'))
|
|
519
|
+
* .map(obj => ({ ...obj, date: Date.parse(obj.epoch) }));
|
|
520
|
+
*
|
|
521
|
+
* const earliestDate = utils.date.startOfDay( utils.agg.min(data, 'date') );
|
|
522
|
+
* const lastDate = utils.date.endOfDay( utils.agg.max(data, 'date') );
|
|
523
|
+
*
|
|
524
|
+
* // binning or lots of other things.
|
|
525
|
+
*
|
|
526
|
+
* return finalResults;
|
|
527
|
+
* });
|
|
528
|
+
*
|
|
529
|
+
* expensiveresults.length = 1023424;
|
|
530
|
+
* ```
|
|
531
|
+
*
|
|
532
|
+
* but sometimes I would rather just skip to the end
|
|
533
|
+
*
|
|
534
|
+
* ```
|
|
535
|
+
* shouldWrite = false; /// we will read from the cache instead,
|
|
536
|
+
* // everything else remains the same
|
|
537
|
+
*
|
|
538
|
+
* expensiveResults = utils.file.useCache(shouldWrite, './cache', 'expensive.json', () => {
|
|
539
|
+
* const data = d3.csvParse(utils.file.readFile('./someFile.csv'))
|
|
540
|
+
* .map(obj => ({ ...obj, date: Date.parse(obj.epoch) }));
|
|
541
|
+
*
|
|
542
|
+
* //-- function can remain untouched,
|
|
543
|
+
* //-- BUT nothing in here will be executed
|
|
544
|
+
* //-- since we are reading from the cache
|
|
545
|
+
* });
|
|
546
|
+
*
|
|
547
|
+
* //-- completely transparent to the runner
|
|
548
|
+
* expensiveresults.length = 1023424;
|
|
549
|
+
* ```
|
|
550
|
+
*
|
|
551
|
+
* @param {Boolean} shouldWrite - whether we should write to the cache (true) or read from the cache (false)
|
|
552
|
+
* @param {String} cachePath - Path to the cache folder, ex: './cache'
|
|
553
|
+
* @param {String} cacheFile - Filename of the cache file to use for this execution, ex: 'ExecutionsPerMin.js'
|
|
554
|
+
* @param {Function} expensiveFn - function that returns the results to be stored in the cache
|
|
555
|
+
* @param {Object} fsOptions - options to use when writing or reading files
|
|
556
|
+
* @returns {any} - either the deserialized json from the cache or the results from the expensive function
|
|
557
|
+
* @see {@link module:file.readJSON|file.readJSON} - reads a local JSON file
|
|
558
|
+
* @see {@link module:file.writeJSON|file.writeJSON} - writes to a JSON file
|
|
559
|
+
* @see {@link module:ijs.useCache|ijs.useCache} - similar idea - but supports promises
|
|
560
|
+
*/
|
|
561
|
+
module.exports.useCache = function useCache(shouldWrite, cachePath, cacheFile, expensiveFn, fsOptions = null) {
|
|
562
|
+
const ensureEndsWithSlash = (str) => str.endsWith('/') ? str : `${str}/`;
|
|
563
|
+
const cacheFilePath = `${ensureEndsWithSlash(cachePath)}${cacheFile}`;
|
|
564
|
+
|
|
565
|
+
if (!shouldWrite) {
|
|
566
|
+
const cleanOptions = { ...fsOptions, formatter: FileUtil.cacheDeserializer };
|
|
567
|
+
const results = FileUtil.readJSON(cacheFilePath, cleanOptions);
|
|
568
|
+
return results;
|
|
483
569
|
}
|
|
484
570
|
|
|
571
|
+
const results = expensiveFn();
|
|
572
|
+
|
|
573
|
+
const cleanOptions = { ...fsOptions, formatter: null }; // FileUtil.cacheSerializer not needed
|
|
574
|
+
|
|
575
|
+
FileUtil.writeJSON(cacheFilePath, results, cleanOptions);
|
|
576
|
+
|
|
485
577
|
return results;
|
|
486
578
|
};
|
|
487
|
-
*/
|
package/src/hashMap.js
CHANGED
|
@@ -83,12 +83,11 @@ module.exports.add = function add(map, key, value) {
|
|
|
83
83
|
* @param {any} functor.value - the first argument is the current value
|
|
84
84
|
* @param {any} functor.key - the second argument is the key passed
|
|
85
85
|
* @param {any} functor.map - the third argument is the map being acted upon
|
|
86
|
-
* @param {any} [setKey = null] - optional separate key to use to set the updated value
|
|
87
86
|
* @returns {Map}
|
|
88
87
|
*/
|
|
89
|
-
module.exports.getSet = function getSet(map, key, functor
|
|
90
|
-
const
|
|
91
|
-
map.set(
|
|
88
|
+
module.exports.getSet = function getSet(map, key, functor) {
|
|
89
|
+
const currentValue = map.has(key) ? map.get(key) : undefined;
|
|
90
|
+
map.set(key, functor(currentValue, key, map));
|
|
92
91
|
return map;
|
|
93
92
|
};
|
|
94
93
|
module.exports.update = module.exports.getSet;
|
package/src/ijs.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const uuid = require('uuid').v4;
|
|
4
4
|
|
|
5
|
+
const FileUtil = require('./file');
|
|
6
|
+
|
|
5
7
|
require('./_types/global');
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -27,6 +29,8 @@ require('./_types/global');
|
|
|
27
29
|
* * {@link module:ijs.noOutputNeeded|ijs.noOutputNeeded} - clears the output to declutter results (like importing libraries, or functions)
|
|
28
30
|
* * {@link module:ijs.initializePageBreaks|ijs.initializePageBreaks} - call at least once to allow pageBreaks when rendering PDFs
|
|
29
31
|
* * {@link module:ijs.printPageBreak|ijs.printPageBreak} - call to print a page break when rendering PDFs
|
|
32
|
+
* * using a cache for long running executions
|
|
33
|
+
* * {@link module:ijs.useCache|ijs.useCache()} - perform an expensive calculation and write to a cache, or read from the cache transparently
|
|
30
34
|
*
|
|
31
35
|
* For example:
|
|
32
36
|
*
|
|
@@ -662,3 +666,102 @@ module.exports.printPageBreak = function printPageBreak() {
|
|
|
662
666
|
|
|
663
667
|
context.$$.html('<div class="pagebreak"></div>');
|
|
664
668
|
};
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* For very long or time-intensive executions, sometimes it is better to cache the results
|
|
672
|
+
* than to execute them every single time.
|
|
673
|
+
*
|
|
674
|
+
* Note that this supports promises, and can be a bit harder to understand.
|
|
675
|
+
*
|
|
676
|
+
* As opposed to {@link module:file.useCache|file.useCache} - which works synchronously
|
|
677
|
+
*
|
|
678
|
+
* ```
|
|
679
|
+
* shouldWrite = true; /// we will write to the cache with the results from the execution
|
|
680
|
+
* utils.file.useCache(shouldWrite, './cache', 'expensive.json', () => {
|
|
681
|
+
* //-- all the items to cache
|
|
682
|
+
* return Promise.resolve()
|
|
683
|
+
* .then(() => ajax.retrieve(...))
|
|
684
|
+
* .then((results) => {
|
|
685
|
+
* const data = results
|
|
686
|
+
* .map(obj => ({ ...obj, date: Date.parse(obj.epoch) }));
|
|
687
|
+
* .. other things to do
|
|
688
|
+
* return data;
|
|
689
|
+
* });
|
|
690
|
+
* })
|
|
691
|
+
* //-- using the information AFTER the expensive function or retrieval from cache
|
|
692
|
+
* //-- this can ALSO be run in a subsequence cell
|
|
693
|
+
* .then((results) => {
|
|
694
|
+
* expensiveresults = results;
|
|
695
|
+
* conosole.log(`expensiveResults.length: ${results.length}`);
|
|
696
|
+
* });
|
|
697
|
+
* ```
|
|
698
|
+
*
|
|
699
|
+
* but sometimes I would rather just skip to the end
|
|
700
|
+
*
|
|
701
|
+
* ```
|
|
702
|
+
* shouldWrite = false; /// we will read from the cache instead,
|
|
703
|
+
* // everything else remains the same
|
|
704
|
+
*
|
|
705
|
+
* utils.file.useCache(shouldWrite, './cache', 'expensive.json', () => {
|
|
706
|
+
* //-- all the items to cache
|
|
707
|
+
* return Promise.resolve()
|
|
708
|
+
* ... blah blah - none of this will get executed
|
|
709
|
+
* })
|
|
710
|
+
* //-- using the information AFTER the expensive function or retrieval from cache
|
|
711
|
+
* //-- this can ALSO be run in a subsequence cell
|
|
712
|
+
* .then((results) => {
|
|
713
|
+
* expensiveresults = results;
|
|
714
|
+
* conosole.log(`expensiveResults.length: ${results.length}`);
|
|
715
|
+
* });
|
|
716
|
+
*
|
|
717
|
+
* //-- completely transparent to the runner
|
|
718
|
+
* expensiveresults.length = 1023424;
|
|
719
|
+
* ```
|
|
720
|
+
*
|
|
721
|
+
* @param {Boolean} shouldWrite - whether we should write to the cache (true) or read from the cache (false)
|
|
722
|
+
* @param {String} cachePath - Path to the cache folder, ex: './cache'
|
|
723
|
+
* @param {String} cacheFile - Filename of the cache file to use for this execution, ex: 'ExecutionsPerMin.js'
|
|
724
|
+
* @param {Function} expensiveFn - function that returns the results to be stored in the cache
|
|
725
|
+
* @param {Object} fsOptions - options to use when writing or reading files
|
|
726
|
+
* @returns {any} - either the deserialized json from the cache or the results from the expensive function
|
|
727
|
+
* @see {@link module:file.readJSON|file.readJSON} - reads a local JSON file
|
|
728
|
+
* @see {@link module:file.writeJSON|file.writeJSON} - writes to a JSON file
|
|
729
|
+
* @see {@link module:file.useCache|file.useCache} - can be much easier than using promises
|
|
730
|
+
*/
|
|
731
|
+
module.exports.useCache = async function useCache(shouldWrite, cachePath, cacheFile, expensiveFn, fsOptions = null) {
|
|
732
|
+
const ensureEndsWithSlash = (str) => str.endsWith('/') ? str : `${str}/`;
|
|
733
|
+
const cacheFilePath = `${ensureEndsWithSlash(cachePath)}${cacheFile}`;
|
|
734
|
+
const context = IJSUtils.detectContext();
|
|
735
|
+
|
|
736
|
+
if (!shouldWrite) {
|
|
737
|
+
const cleanOptions = { ...fsOptions, formatter: FileUtil.cacheDeserializer };
|
|
738
|
+
//-- we read the info and be done with it
|
|
739
|
+
//context.console.log(`before retrieving cache:${cacheFilePath}`);
|
|
740
|
+
const results = FileUtil.readJSON(cacheFilePath, cleanOptions);
|
|
741
|
+
//context.console.log(`after retrieving cache`);
|
|
742
|
+
return Promise.resolve(results);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (!context) {
|
|
746
|
+
throw (Error('IJSUtils.async must be run within iJavaScript. Otherwise, use normal async methods'));
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
context.$$.async();
|
|
750
|
+
|
|
751
|
+
try {
|
|
752
|
+
context.console.log('before starting expensive fn');
|
|
753
|
+
const results = await expensiveFn(context.$$, context.console);
|
|
754
|
+
context.console.log('AFTER starting expensive fn');
|
|
755
|
+
|
|
756
|
+
FileUtil.writeJSON(cacheFilePath, results);
|
|
757
|
+
// context.$$.sendResult('success');
|
|
758
|
+
return results;
|
|
759
|
+
} catch (err) {
|
|
760
|
+
context.console.error('error occurred');
|
|
761
|
+
context.console.error(err);
|
|
762
|
+
context.$$.sendResult(err);
|
|
763
|
+
|
|
764
|
+
// return Promise.reject(err);
|
|
765
|
+
throw err;
|
|
766
|
+
}
|
|
767
|
+
};
|
package/src/object.js
CHANGED
|
@@ -26,6 +26,7 @@ const FormatUtils = require('./format');
|
|
|
26
26
|
* * {@link module:object.extractObjectProperties|extractObjectProperties(list, propertyNameOrFnMap)} - extracts multiple propertie or fn across all objects in list.
|
|
27
27
|
* * Apply deep values safely
|
|
28
28
|
* * {@link module:object.assign|objAssign(object, property, value)} - Applies properties to an object in functional programming style.
|
|
29
|
+
* * {@link module:object.getSet|getSet(object, property, functor)} - calls a function with the current value on an object, allowing for decrementing/incrementing/etc.
|
|
29
30
|
* * {@link module:object.augment|augment(object, augmentFn)} - Applies properties to an object similar to Map
|
|
30
31
|
* * {@link module:object.assignEntities|objAssignEntities(object, [property, value])} - Applies properties to an object using Array values - [key,value]
|
|
31
32
|
* * {@link module:object.setPropertyDefaults|setPropertyDefaults()} - sets values for objects that don't currently have the property
|
|
@@ -169,6 +170,53 @@ module.exports.assignEntities = function objAssignEntities(obj, entities) {
|
|
|
169
170
|
};
|
|
170
171
|
module.exports.objAssignEntities = module.exports.assignEntities;
|
|
171
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Use this for times where you want to update a value
|
|
175
|
+
*
|
|
176
|
+
*
|
|
177
|
+
* ```
|
|
178
|
+
* key = 'somethingToIncrement';
|
|
179
|
+
* defaultValue = null;
|
|
180
|
+
*
|
|
181
|
+
* const initialObject = {};
|
|
182
|
+
* initialObject[key] = defaultValue;
|
|
183
|
+
*
|
|
184
|
+
* // { somethingToIncrement: null }
|
|
185
|
+
*
|
|
186
|
+
* const functor = (value) => { //, key, map) => {
|
|
187
|
+
* if (!value) return 1;
|
|
188
|
+
* return value + 1;
|
|
189
|
+
* };
|
|
190
|
+
*
|
|
191
|
+
* utils.object.getSet(initialObject, key, functor);
|
|
192
|
+
*
|
|
193
|
+
* // equivalent to
|
|
194
|
+
* // intitialObject[key] = intitialObject[key] ? initialObject[key] + 1 : 1`
|
|
195
|
+
* // but in a way that is repeatable across many values
|
|
196
|
+
*
|
|
197
|
+
* utils.object.getSet(initialObject, key, functor);
|
|
198
|
+
* utils.object.getSet(initialObject, key, functor);
|
|
199
|
+
* utils.object.getSet(initialObject, key, functor);
|
|
200
|
+
* utils.object.getSet(initialObject, key, functor);
|
|
201
|
+
*
|
|
202
|
+
* initialObject.get(key); // 5
|
|
203
|
+
* ```
|
|
204
|
+
*
|
|
205
|
+
* @param {Map} map - map to get and set values from
|
|
206
|
+
* @param {any} key - they key to GET and SET the value (unless setKey is provided)
|
|
207
|
+
* @param {Function} functor - the function called with the arguments below - returning the value to set
|
|
208
|
+
* @param {any} functor.value - the first argument is the current value
|
|
209
|
+
* @param {any} functor.key - the second argument is the key passed
|
|
210
|
+
* @param {any} functor.map - the third argument is the map being acted upon
|
|
211
|
+
* @returns {Map}
|
|
212
|
+
*/
|
|
213
|
+
module.exports.getSet = function getSet(obj, field, functor) {
|
|
214
|
+
const currentValue = Object.hasOwn(obj, field) ? obj[field] : undefined;
|
|
215
|
+
obj[field] = functor(currentValue, field, obj);
|
|
216
|
+
return obj;
|
|
217
|
+
};
|
|
218
|
+
module.exports.update = module.exports.getSet;
|
|
219
|
+
|
|
172
220
|
/**
|
|
173
221
|
* Runs a map over a collection, and adds properties the the objects.
|
|
174
222
|
*
|
|
@@ -413,9 +461,32 @@ module.exports.cleanPropertyName = function cleanPropertyName(property) {
|
|
|
413
461
|
return cleanProperty;
|
|
414
462
|
};
|
|
415
463
|
|
|
416
|
-
|
|
464
|
+
/**
|
|
465
|
+
* Renames properties on an object with a list of original keys and new keys.
|
|
466
|
+
*
|
|
467
|
+
* For example:
|
|
468
|
+
*
|
|
469
|
+
* ```
|
|
470
|
+
* myData = [{ _time: '...', 'series001': 1, 'series002': 2 }];
|
|
471
|
+
*
|
|
472
|
+
* originalKeys = utils.object.keys(myData);
|
|
473
|
+
* // ['series001', 'series002'];
|
|
474
|
+
*
|
|
475
|
+
* myMap = new Map([['series001': 'Alpha'], ['series002', 'Bravo']]);
|
|
476
|
+
* newKeys = utils.format.replaceStrings(originalKeys, myMap);
|
|
477
|
+
* // ['Alpha', 'Bravo'];
|
|
478
|
+
*
|
|
479
|
+
* utils.object.renamePropertiesFromList(myData, originalKeys, newKeys);
|
|
480
|
+
* // [{ _time: '...', 'Alpha': 1, 'Bravo': 2 }];
|
|
481
|
+
*
|
|
482
|
+
* @param {Object[]} objects - objects to reassign - likely from a CSV
|
|
483
|
+
* @param {String[]} originalKeys - list of keys to change FROM
|
|
484
|
+
* @param {String[]} updatedKeys - list of keys to change TO
|
|
485
|
+
* @returns {Object[]}
|
|
486
|
+
*/
|
|
487
|
+
module.exports.renamePropertiesFromList = function renamePropertiesFromList(object, originalKeys, targetKeys) {
|
|
417
488
|
const result = { ...object };
|
|
418
|
-
originalKeys.forEach((originalKey, index) => {
|
|
489
|
+
Array.from(originalKeys).forEach((originalKey, index) => {
|
|
419
490
|
const targetKey = targetKeys[index];
|
|
420
491
|
if (targetKey !== originalKey) {
|
|
421
492
|
result[targetKey] = result[originalKey];
|
|
@@ -440,10 +511,10 @@ module.exports.renameProperties = function renameProperties(objects, propertyTra
|
|
|
440
511
|
|
|
441
512
|
if (Array.isArray(objects)) {
|
|
442
513
|
return objects.map(
|
|
443
|
-
(object) =>
|
|
514
|
+
(object) => ObjectUtils.renamePropertiesFromList(object, originalKeys, targetKeys)
|
|
444
515
|
);
|
|
445
516
|
}
|
|
446
|
-
return
|
|
517
|
+
return ObjectUtils.renamePropertiesFromList(objects, originalKeys, targetKeys);
|
|
447
518
|
};
|
|
448
519
|
|
|
449
520
|
const collapseSpecificObject = function collapseSpecificObject(sourceObj, targetObj, depth) {
|