jupyter-ijavascript-utils 1.38.0 → 1.40.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 +3 -1
- package/Dockerfile +1 -1
- package/README.md +3 -1
- package/package.json +1 -1
- package/src/array.js +209 -2
- package/src/format.js +55 -0
- package/src/object.js +210 -18
package/DOCS.md
CHANGED
|
@@ -75,7 +75,9 @@ Give it a try here:
|
|
|
75
75
|
|
|
76
76
|
## What's New
|
|
77
77
|
|
|
78
|
-
* 1.
|
|
78
|
+
* 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
|
|
79
|
+
* 1.39 - {@link module:format.extractWords|format.exportWords} - to identify distinct words in strings using unicode character properties
|
|
80
|
+
* 1.38 - {@link module:object.extractObjectProperty|object.extractObjectProperty} / {@link module:object.applyPropertyValue|object.applyPropertyValue} to allow for extracting values from arrays, transforming them on a separate process and applying them back
|
|
79
81
|
* 1.37 - {@link module:format.replaceString|format.replaceString} as convenience for replacing only a single string.
|
|
80
82
|
* 1.36 - {@link module:format.replaceStrings|format.replaceStrings} to allow for replacement dictionaries and tuplets
|
|
81
83
|
* 1.35 - {@link module:object.extractObjectProperties|extractObjectProperties} / {@link module:object.extractObjectProperty|extractObjectProperty} - to do horizontal transposes on objects
|
package/Dockerfile
CHANGED
package/README.md
CHANGED
|
@@ -55,7 +55,9 @@ 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.
|
|
58
|
+
* 1.40 - array.extract and array.applyArrayValue to allow for extracting values from arrays, transforming them on a separate process and applying them back
|
|
59
|
+
* 1.39 - format.exportWords - to identify distinct words in strings using unicode character properties
|
|
60
|
+
* 1.38 - object.extract / object.apply
|
|
59
61
|
* 1.37 - {@link module:format.replaceString|format.replaceString} as convenience for replacing only a single string.
|
|
60
62
|
* 1.36 - replaceStrings to allow for replacement dictionaries and tuplets
|
|
61
63
|
* 1.35 - object.extractObjectProperties / object.extractObjectProperty - to do horizontal transposes on objects
|
package/package.json
CHANGED
package/src/array.js
CHANGED
|
@@ -27,6 +27,10 @@ require('./_types/global');
|
|
|
27
27
|
* * {@link module:array.pickRows|array.pickRows} - picks a row from a 2d array
|
|
28
28
|
* * {@link module:array.pickColumns|array.pickColumns} - picks a column from a 2d array
|
|
29
29
|
* * {@link module:array.pick|array.pick} - picks either/or rows and columns
|
|
30
|
+
* * {@link module:array.extract|array.extract} - synonym to array.pick to pick either a row or column from an array
|
|
31
|
+
* * Applying a value
|
|
32
|
+
* * {@link module:array.applyArrayValue|array.applyArrayValue} - applies a value deeply into an array safely
|
|
33
|
+
* * {@link module:array.applyArrayValues|array.applyArrayValues} - applies a value / multiple values deeply into an array safely
|
|
30
34
|
* * Understanding Values
|
|
31
35
|
* * {@link module:array.isMultiDimensional|array.isMultiDimensional} - determines if an array is multi-dimensional
|
|
32
36
|
*
|
|
@@ -150,6 +154,7 @@ module.exports.peekLast = function peekLast(targetArray, defaultVal = null) {
|
|
|
150
154
|
* @param {Array} array2d - 2d array to pick from [row][column]
|
|
151
155
|
* @param {...Number} rowIndices - Indexes of the row to return, [0...length-1]
|
|
152
156
|
* @returns - Array with only those rows
|
|
157
|
+
* @see {@link module:array.pick|array.pick} - pick either rows or columns
|
|
153
158
|
* @example
|
|
154
159
|
* data = [
|
|
155
160
|
* ['john', 23, 'purple'],
|
|
@@ -179,6 +184,7 @@ module.exports.pickRows = function pickRows(array2d, ...rowIndices) {
|
|
|
179
184
|
* @param {Array} array2d - 2d array to pick from [row][column]
|
|
180
185
|
* @param {...any} columns - Indexes of the columns to pick the values from: [0...row.length-1]
|
|
181
186
|
* @returns - Array with all rows, and only those columns
|
|
187
|
+
* @see {@link module:array.pick|array.pick} - pick either rows or columns
|
|
182
188
|
* @example
|
|
183
189
|
* data = [
|
|
184
190
|
* ['john', 23, 'purple'],
|
|
@@ -210,8 +216,9 @@ module.exports.pickColumns = function pickColumns(array2d, ...columns) {
|
|
|
210
216
|
* @param {Number[]} [options.rows = null] - indices of the rows to pick
|
|
211
217
|
* @param {Number[]} [options.columns = null] - indices of the columns to pick.
|
|
212
218
|
* @returns {Array} - 2d array of only the rows and columns chosen.
|
|
213
|
-
* @see {@link module:
|
|
214
|
-
* @see {@link module:
|
|
219
|
+
* @see {@link module:array.pickRows|array.pickRows} - picking rows
|
|
220
|
+
* @see {@link module:array.pickColumns|array.pickColumns} - picking columns
|
|
221
|
+
* @see {@link module:array.applyArrayValues|array.applyArrayValues} - applies a value / multiple values deeply into an array safely
|
|
215
222
|
* @returns - 2dArray of the columns and rows requested
|
|
216
223
|
* @example
|
|
217
224
|
* data = [
|
|
@@ -250,6 +257,206 @@ module.exports.pick = function pick(array2d, options) {
|
|
|
250
257
|
return results;
|
|
251
258
|
};
|
|
252
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Convenience function for picking specific rows and columns from a 2d array.
|
|
262
|
+
*
|
|
263
|
+
* Alias of {@link module:array.pick|array.pick}
|
|
264
|
+
*
|
|
265
|
+
* Please also see [Danfo.js](https://danfo.jsdata.org/) for working with DataFrames.
|
|
266
|
+
*
|
|
267
|
+
* @param {Array} array2d - 2d array to pick from [row][column]
|
|
268
|
+
* @param {Object} options - options on which to pick
|
|
269
|
+
* @param {Number[]} [options.rows = null] - indices of the rows to pick
|
|
270
|
+
* @param {Number[]} [options.columns = null] - indices of the columns to pick.
|
|
271
|
+
* @returns {Array} - 2d array of only the rows and columns chosen.
|
|
272
|
+
* @see {@link module:array.pickRows} - picking rows
|
|
273
|
+
* @see {@link module:array.pickColumns} - picking columns
|
|
274
|
+
* @returns - 2dArray of the columns and rows requested
|
|
275
|
+
* @example
|
|
276
|
+
* data = [
|
|
277
|
+
* ['john', 23, 'purple'],
|
|
278
|
+
* ['jane', 32, 'red'],
|
|
279
|
+
* ['ringo', 27, 'green']
|
|
280
|
+
* ];
|
|
281
|
+
*
|
|
282
|
+
* utils.array.pick(data, {rows: [0, 1]});
|
|
283
|
+
* //-- [['john', 23, 'purple'], ['jane', 32, 'red']];
|
|
284
|
+
*
|
|
285
|
+
* utils.array.pick(data, {columns: [0, 2]});
|
|
286
|
+
* //-- [['john', 'purple'], ['jane', 'red'], ['ringo', 'green']];
|
|
287
|
+
*
|
|
288
|
+
* utils.array.pick(data, {rows:[0, 1], columns:[0, 2]});
|
|
289
|
+
* //-- [['john', 'purple'], ['jane', 'red']];
|
|
290
|
+
*/
|
|
291
|
+
module.exports.extract = module.exports.pick;
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Applies deeply onto an array safely - in-place using dot-notation paths
|
|
295
|
+
* even if the child paths don't exist.
|
|
296
|
+
*
|
|
297
|
+
* While tthis can be as simple as safely applying a value even if targetObj may be null
|
|
298
|
+
*
|
|
299
|
+
* ```
|
|
300
|
+
* targetObj = [1, 2, null, 4, 5];
|
|
301
|
+
*
|
|
302
|
+
* utils.object.applyPropertyValue(targetObj, '[2]', 3);
|
|
303
|
+
* // [1, 2, 3, 4, 5]
|
|
304
|
+
* // equivalent to targetObj[2] = 3;
|
|
305
|
+
* ```
|
|
306
|
+
*
|
|
307
|
+
* This is much more safely working with deeply nested objects
|
|
308
|
+
*
|
|
309
|
+
* ```
|
|
310
|
+
* targetObj = [{
|
|
311
|
+
* name: 'john smith',
|
|
312
|
+
* class: {
|
|
313
|
+
* name: 'ECON_101',
|
|
314
|
+
* professor: {
|
|
315
|
+
* last_name: 'Winklemeyer'
|
|
316
|
+
* }
|
|
317
|
+
* }
|
|
318
|
+
* }];
|
|
319
|
+
*
|
|
320
|
+
* utils.object.applyPropertyValue(targetObj, '[0].class.professor.first_name', 'René');
|
|
321
|
+
* // [{
|
|
322
|
+
* // name: 'john smith',
|
|
323
|
+
* // class: {
|
|
324
|
+
* // name: 'ECON_101',
|
|
325
|
+
* // professor: {
|
|
326
|
+
* // last_name: 'Winklemeyer',
|
|
327
|
+
* // first_name: 'René' // <- Added
|
|
328
|
+
* // }
|
|
329
|
+
* // }
|
|
330
|
+
* // }];
|
|
331
|
+
* ```
|
|
332
|
+
*
|
|
333
|
+
* or creating intermediary objects along the path - if they did not exist first.
|
|
334
|
+
*
|
|
335
|
+
* ```
|
|
336
|
+
* targetObj = [{
|
|
337
|
+
* name: 'john smith'
|
|
338
|
+
* }];
|
|
339
|
+
* utils.object.applyPropertyValue(targetObj, '[0].class.professor.first_name', 'René');
|
|
340
|
+
* [{
|
|
341
|
+
* name: 'john smith',
|
|
342
|
+
* class: {
|
|
343
|
+
* professor: {
|
|
344
|
+
* first_name: 'René'
|
|
345
|
+
* }
|
|
346
|
+
* }
|
|
347
|
+
* }];
|
|
348
|
+
* ```
|
|
349
|
+
*
|
|
350
|
+
* @param {Array} collection - array to apply the value to
|
|
351
|
+
* @param {string} path - dot notation path to set the value, ex: 'geo', or 'states[0].prop'
|
|
352
|
+
* @param {any} value - value to set
|
|
353
|
+
* @returns {Array} - the base array
|
|
354
|
+
* @see {@link module:array.pick|array.pick} - to pick a row or column into an array
|
|
355
|
+
* @see {@link module:array.applyArrayValues|array.applyArrayValues} - applies an array safely and deeply onto another array of values
|
|
356
|
+
*/
|
|
357
|
+
module.exports.applyArrayValue = function applyArrayValue(collection, path, value) {
|
|
358
|
+
// const signature = 'applyArrayValue(collection, path, value)';
|
|
359
|
+
|
|
360
|
+
if (!collection) return collection;
|
|
361
|
+
if (!path) return collection;
|
|
362
|
+
|
|
363
|
+
const cleanPath = String(path)
|
|
364
|
+
.replace(/\[/g, '.')
|
|
365
|
+
.replace(/\]/g, '.')
|
|
366
|
+
.replace(/[.]+/g, '.')
|
|
367
|
+
.replace(/^[.]+/, '')
|
|
368
|
+
.replace(/[.]$/, '');
|
|
369
|
+
|
|
370
|
+
const splitPath = cleanPath.split('.');
|
|
371
|
+
const terminalIndex = splitPath.length - 1;
|
|
372
|
+
|
|
373
|
+
return splitPath
|
|
374
|
+
.reduce((currentVal, prop, currentIndex) => {
|
|
375
|
+
//-- can no longer occur
|
|
376
|
+
// if (!prop) throw Error(`${signature}:Unable to set value with path:${path}`);
|
|
377
|
+
|
|
378
|
+
const isLeaf = currentIndex === terminalIndex;
|
|
379
|
+
if (isLeaf) {
|
|
380
|
+
// eslint-disable-next-line no-param-reassign
|
|
381
|
+
currentVal[prop] = value;
|
|
382
|
+
// if (value === undefined) {
|
|
383
|
+
// delete currentVal[prop];
|
|
384
|
+
// } else {
|
|
385
|
+
// currentVal[prop] = value;
|
|
386
|
+
// }
|
|
387
|
+
return collection;
|
|
388
|
+
}
|
|
389
|
+
//-- not a leaf
|
|
390
|
+
if (!currentVal[prop]) {
|
|
391
|
+
// eslint-disable-next-line no-param-reassign
|
|
392
|
+
currentVal[prop] = {};
|
|
393
|
+
}
|
|
394
|
+
return currentVal[prop];
|
|
395
|
+
}, collection);
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Converse from the extractPropertyValue, this takes a value / set of values
|
|
400
|
+
* and applies the values for each index in the collection.
|
|
401
|
+
*
|
|
402
|
+
* for example:
|
|
403
|
+
*
|
|
404
|
+
* ```
|
|
405
|
+
* weather = [{ id: 1, city: 'Seattle', month: 'Aug', precip: 0.87 },
|
|
406
|
+
* { id: 3, city: 'New York', month: 'Apr', precip: 3.94 },
|
|
407
|
+
* { id: 6, city: 'Chicago', month: 'Apr', precip: 3.62 }];
|
|
408
|
+
*
|
|
409
|
+
* cities = utils.object.extractObjectProperty('city');
|
|
410
|
+
* // ['Seattle', 'New York', 'Chicago'];
|
|
411
|
+
*
|
|
412
|
+
* //-- async process to geocode
|
|
413
|
+
* geocodedCities = geocodeCity(cities);
|
|
414
|
+
* // [{ city: 'Seattle', state: 'WA', country: 'USA' },
|
|
415
|
+
* // { city: 'New York', state: 'NY', country: 'USA' },
|
|
416
|
+
* // { city: 'Chicago', state: 'IL', country: 'USA' }]
|
|
417
|
+
*
|
|
418
|
+
* utils.applyArrayValues(weather, 'geo', geocodedCities);
|
|
419
|
+
* // [{ id: 1, city: 'Seattle', month: 'Aug', precip: 0.87, geo: { city: 'Seattle', state: 'WA', country: 'USA' } },
|
|
420
|
+
* // { id: 3, city: 'New York', month: 'Apr', precip: 3.94, geo: { city: 'New York', state: 'NY', country: 'USA' } },
|
|
421
|
+
* // { id: 6, city: 'Chicago', month: 'Apr', precip: 3.62, geo: { city: 'Chicago', state: 'IL', country: 'USA' } }];
|
|
422
|
+
*
|
|
423
|
+
* Note that traditional [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
|
|
424
|
+
* works best for if you are working with objects completely in memory.
|
|
425
|
+
*
|
|
426
|
+
* But this helps quite a bit if the action of mapping / transforming values
|
|
427
|
+
* needs to be separate from the extraction / application of values back.
|
|
428
|
+
*
|
|
429
|
+
* @param {Array} collection - array to apply the value to on each index
|
|
430
|
+
* @param {string} path - dot notation path to set the value within each index, ex: 'geo', or 'states[0].prop'
|
|
431
|
+
* @param {any} value - the value that should be set at that path.
|
|
432
|
+
* @returns {Object}
|
|
433
|
+
* @see {@link module:array.applyArrayValue|array.applyArrayValue} - to apply a single value to a single object
|
|
434
|
+
* @see {@link module:array.pick|array.pick} - to pick a row or column into an array
|
|
435
|
+
*/
|
|
436
|
+
module.exports.applyArrayValues = function applyArrayValues(collection, path, valueList) {
|
|
437
|
+
// const signature = 'applyValue(objectList, path, valueList)';
|
|
438
|
+
if (!collection || !path) {
|
|
439
|
+
//-- do nothing
|
|
440
|
+
return collection;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const cleanCollection = Array.isArray(collection) ? collection : [collection];
|
|
444
|
+
const cleanValueList = Array.isArray(valueList) ? valueList : Array(cleanCollection.length).fill(valueList);
|
|
445
|
+
|
|
446
|
+
// if (cleanCollection.length !== cleanValueList) throw Error(
|
|
447
|
+
// `${signature}: objectList.length[${cleanCollection.length}] does not match valueList.length[${cleanValueList.length}]`
|
|
448
|
+
// );
|
|
449
|
+
const minLength = Math.min(cleanCollection.length, cleanValueList.length);
|
|
450
|
+
|
|
451
|
+
for (let i = 0; i < minLength; i += 1) {
|
|
452
|
+
const obj = cleanCollection[i];
|
|
453
|
+
const val = cleanValueList[i];
|
|
454
|
+
ArrayUtils.applyArrayValue(obj, path, val);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return collection;
|
|
458
|
+
};
|
|
459
|
+
|
|
253
460
|
/**
|
|
254
461
|
* Creates an array of a specific size and default value
|
|
255
462
|
*
|
package/src/format.js
CHANGED
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
* * {@link module:format.parseNumber|format.parseNumber(val, locale)} - converts a value to a number
|
|
42
42
|
* * Identifying values
|
|
43
43
|
* * {@link module:format.isEmptyValue|format.isEmptyValue} - determine if a value is not 'empty'
|
|
44
|
+
* * Extracting values
|
|
45
|
+
* * {@link module:format.extractWords|format.extractWords} - to extract the words from a string
|
|
44
46
|
*
|
|
45
47
|
* @module format
|
|
46
48
|
* @exports format
|
|
@@ -1286,3 +1288,56 @@ module.exports.replaceString = function replaceString(targetStr, stringTupletsOr
|
|
|
1286
1288
|
}
|
|
1287
1289
|
return FormatUtils.replaceStrings([targetStr], stringTupletsOrMap)[0];
|
|
1288
1290
|
};
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Identify an array of words each from a string.
|
|
1294
|
+
*
|
|
1295
|
+
* Note that this uses the new [Unicode Properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Unicode_character_class_escape)
|
|
1296
|
+
* using `\p{L}` to identify characters based on unicode properties.
|
|
1297
|
+
*
|
|
1298
|
+
* For example:
|
|
1299
|
+
*
|
|
1300
|
+
* ```
|
|
1301
|
+
* strs = 'I am Modern "major-general".';
|
|
1302
|
+
* FormatUtils.extractWords(strs);
|
|
1303
|
+
* // ['I', 'am', 'Modern', 'major', 'general'];
|
|
1304
|
+
* ```
|
|
1305
|
+
*
|
|
1306
|
+
* you can also include additional characters that will no longer be considered word boundaries.
|
|
1307
|
+
*
|
|
1308
|
+
* ```
|
|
1309
|
+
* strs = 'I am Modern "major-general".';
|
|
1310
|
+
* FormatUtils.extractWords(strs);
|
|
1311
|
+
* // ['I', 'am', 'Modern', 'major-general'];
|
|
1312
|
+
* ```
|
|
1313
|
+
*
|
|
1314
|
+
* arrays of strings are also supported
|
|
1315
|
+
*
|
|
1316
|
+
* ```
|
|
1317
|
+
* strs = ['letras mayúsculas de tamaño igual a las minúsculas',
|
|
1318
|
+
* 'الاختراع، ومايكل هارت'];
|
|
1319
|
+
* FormatUtils.extractWords(strs);
|
|
1320
|
+
* // [ 'letras', 'mayúsculas', 'de', 'tamaño', 'igual', 'a', 'las', 'minúsculas', 'الاختراع', 'ومايكل', 'هارت'];
|
|
1321
|
+
* ```
|
|
1322
|
+
*
|
|
1323
|
+
* @param {String|String[]} strToExtractFrom - collection of strings to extract words from
|
|
1324
|
+
* @param {String} additionalNonBreakingCharacters - each char in this string will not be treated as a word boundry
|
|
1325
|
+
* @returns {string[]} - collection of words
|
|
1326
|
+
*/
|
|
1327
|
+
module.exports.extractWords = function extractWords(strToExtractFrom, additionalNonBreakingCharacters) {
|
|
1328
|
+
if (!strToExtractFrom) return strToExtractFrom;
|
|
1329
|
+
|
|
1330
|
+
const cleanNonBreaking = additionalNonBreakingCharacters
|
|
1331
|
+
? additionalNonBreakingCharacters
|
|
1332
|
+
: '';
|
|
1333
|
+
|
|
1334
|
+
const regex = cleanNonBreaking
|
|
1335
|
+
? new RegExp(`[${additionalNonBreakingCharacters}\\p{L}]+`, 'gu')
|
|
1336
|
+
: /\p{L}+/ug; // -- old attempt/[A-Za-zÀ-ÖØ-öø-ÿ]+/g;
|
|
1337
|
+
|
|
1338
|
+
const cleanStrings = Array.isArray(strToExtractFrom)
|
|
1339
|
+
? strToExtractFrom
|
|
1340
|
+
: [strToExtractFrom];
|
|
1341
|
+
|
|
1342
|
+
return cleanStrings.reduce((result, str) => [...result, ...((str || '').match(regex) || [])], []);
|
|
1343
|
+
};
|
package/src/object.js
CHANGED
|
@@ -13,29 +13,33 @@ const FormatUtils = require('./format');
|
|
|
13
13
|
* * {@link module:object.generateSchema|generateSchema()} - generate a schema / describe properties of a list of objects
|
|
14
14
|
* * {@link module:object.findWithoutProperties|findWithoutProperties()} - find objects without ALL the properties specified
|
|
15
15
|
* * {@link module:object.findWithoutProperties|findWithProperties()} - find objects with any of the properties specified
|
|
16
|
-
* * {@link module:object.setPropertyDefaults|setPropertyDefaults()} - sets values for objects that don't currently have the property
|
|
17
16
|
* * {@link module:object.propertyValueSample|propertyValueSample(collection)} - finds non-empty values for all properties found in the collection
|
|
18
|
-
* * Manipulating objects
|
|
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.
|
|
23
|
-
* * {@link module:object.selectObjectProperties|selectObjectProperties()} - keep only specific properties
|
|
24
|
-
* * {@link module:object.filterObjectProperties|filterObjectProperties()} - remove specific properties
|
|
25
|
-
* * {@link module:object.mapProperties|mapProperties(collection, fn, ...properties)} - map multiple properties at once (like parseInt, or toString)
|
|
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.
|
|
28
17
|
* * Fetch child properties from related objects
|
|
29
18
|
* * {@link module:object.fetchObjectProperty|fetchObjectProperty(object, string)} - use dot notation to bring a child property onto a parent
|
|
30
19
|
* * {@link module:object.fetchObjectProperties|fetchObjectProperties(object, string[])} - use dot notation to bring multiple child properties onto a parent
|
|
31
20
|
* * {@link module:object.join|join(array, index, map, fn)} - join a collection against a map by a given index
|
|
32
21
|
* * {@link module:object.joinProperties|join(array, index, map, ...fields)} - join a collection, and copy properties over from the mapped object.
|
|
22
|
+
* * Fetch values safely
|
|
33
23
|
* * {@link module:object.propertyFromList|propertyFromList(array, propertyName)} - fetches a specific property from all objects in a list
|
|
34
24
|
* * {@link module:object.extractObjectProperty|extractObjectProperty(list, propertyNameOrFn)} - extracts a property or fn across all objects in list.
|
|
35
25
|
* * {@link module:object.extractObjectProperties|extractObjectProperties(list, propertyNameOrFnMap)} - extracts multiple propertie or fn across all objects in list.
|
|
26
|
+
* * Apply deep values safely
|
|
27
|
+
* * {@link module:object.assign|objAssign(object, property, value)} - Applies properties to an object in functional programming style.
|
|
28
|
+
* * {@link module:object.augment|augment(object, augmentFn)} - Applies properties to an object similar to Map
|
|
29
|
+
* * {@link module:object.assignEntities|objAssignEntities(object, [property, value])} - Applies properties to an object using Array values - [key,value]
|
|
30
|
+
* * {@link module:object.setPropertyDefaults|setPropertyDefaults()} - sets values for objects that don't currently have the property
|
|
31
|
+
* * {@link module:object.applyPropertyValue|object.applyPropertyValue} - safely apply a value deeply and safely
|
|
32
|
+
* * {@link module:object.applyPropertyValues|object.applyPropertyValues} - apply an array of values safely and deeply against a list of objects.
|
|
33
|
+
* * Manipulating objects
|
|
34
|
+
* * {@link module:object.augmentInherit|augmentInherit(object, augmentFn)} - Applies properties to a collection of objects, 'remembering' the last value - useful for 1d to *D lists.
|
|
35
|
+
* * {@link module:object.selectObjectProperties|selectObjectProperties()} - keep only specific properties
|
|
36
|
+
* * {@link module:object.filterObjectProperties|filterObjectProperties()} - remove specific properties
|
|
37
|
+
* * {@link module:object.mapProperties|mapProperties(collection, fn, ...properties)} - map multiple properties at once (like parseInt, or toString)
|
|
38
|
+
* * {@link module:object.formatProperties|formatProperties(collection, propertyTranslation)} - map specific properties (ex: toString, toNumber, etc)
|
|
39
|
+
* * {@link module:object.union|union(objectList1, objectList2)} - Unites the properties of two collections of objects.
|
|
36
40
|
* * Rename properties
|
|
37
41
|
* * {@link module:object.cleanProperties|cleanProperties()} - correct inaccessible property names in a list of objects - in place
|
|
38
|
-
*
|
|
42
|
+
* * {@link module:object.cleanProperties2|cleanProperties2()} - correct inaccessible property names in a list of objects - on a cloned list
|
|
39
43
|
* * {@link module:object.cleanPropertyNames|cleanPropertyNames()} - create a translation of inaccessible names to accessible ones
|
|
40
44
|
* * {@link module:object.cleanPropertyName|cleanPropertyName()} - create a translation of a specific property name to be accessible.
|
|
41
45
|
* * {@link module:object.renameProperties|renameProperties()} - Use a translation from old property names to new ones
|
|
@@ -518,7 +522,9 @@ module.exports.filterObjectProperties = function filterObjectProperties(list, pr
|
|
|
518
522
|
* @param {Function | String} propertyOrFn - Name of the property or accessor function
|
|
519
523
|
* @returns {Array} - single array the values stored in propertyOrFn across all objects in objectList.
|
|
520
524
|
* @see {@link module:aggregate.unique|unique()} to see all the unique values stored
|
|
521
|
-
* @see {@link module:object.extractObjectProperties|extractObjectProperties
|
|
525
|
+
* @see {@link module:object.extractObjectProperties|object.extractObjectProperties} - to extract into array vectors
|
|
526
|
+
* @see {@link module:object.fetchObjectProperty|object.fetchObjectProperty} - to extract a deep value and optionally throw if not found
|
|
527
|
+
* @see {@link module:object.applyPropertyValue|object.applyPropertyValue} - to apply a single value to a single object using dot notation safely
|
|
522
528
|
*/
|
|
523
529
|
module.exports.extractObjectProperty = function extractObjectProperty(list, propertyOrFn) {
|
|
524
530
|
let cleanList = !list ? [] : Array.isArray(list) ? list : [list];
|
|
@@ -560,6 +566,8 @@ module.exports.extractObjectProperty = function extractObjectProperty(list, prop
|
|
|
560
566
|
* @param {Map<Function | String>} propertyOrFnMap - Name of the property or accessor function
|
|
561
567
|
* @returns {Object} - Object with the keys in the map as properties - extracting the values across all in list.
|
|
562
568
|
* @see {@link module:object.extractObjectProperty|extractObjectProperty(list, propertyNameOrFn)} to see all the values stored for a single property.
|
|
569
|
+
* @see {@link module:object.applyPropertyValues|object.applyPropertyValues} - to safely and deeply apply the list of values extracted to a list of objects.
|
|
570
|
+
* @see {@link module:object.fetchObjectProperties} - to fetch multiple properties at once into objects
|
|
563
571
|
*/
|
|
564
572
|
module.exports.extractObjectProperties = function extractObjectProperties(list, propertyOrFnMap) {
|
|
565
573
|
let propertyEntries = [];
|
|
@@ -585,8 +593,6 @@ module.exports.extractObjectProperties = function extractObjectProperties(list,
|
|
|
585
593
|
const results = {};
|
|
586
594
|
propertyEntries.forEach(([propertyName, propertyOrFn]) => {
|
|
587
595
|
results[propertyName] = ObjectUtils.extractObjectProperty(list, propertyOrFn || propertyName);
|
|
588
|
-
// const fn = ObjectUtils.evaluateFunctionOrProperty(propertyOrFn);
|
|
589
|
-
// results[propertyName] = cleanList.map(fn);
|
|
590
596
|
});
|
|
591
597
|
|
|
592
598
|
return results;
|
|
@@ -600,13 +606,27 @@ module.exports.extractObjectProperties = function extractObjectProperties(list,
|
|
|
600
606
|
|
|
601
607
|
/**
|
|
602
608
|
* Fetches multiple properties from an object or list of objects.
|
|
609
|
+
*
|
|
610
|
+
* ```
|
|
611
|
+
* testObj = {
|
|
612
|
+
* name: 'john',
|
|
613
|
+
* courses: [{ name: 'econ-101' }]
|
|
614
|
+
* }
|
|
615
|
+
* utils.object.fetchObjectProperty(testObj,
|
|
616
|
+
* { 'courseName': 'courses[0].?name', personName: 'name' });
|
|
617
|
+
* // { courseName: 'econ-101', personName: 'john' }
|
|
618
|
+
* ```
|
|
619
|
+
*
|
|
603
620
|
* @param {Object | Object[]} list - collection of objects to reduce
|
|
604
|
-
* @param {
|
|
621
|
+
* @param {Object<String,any>} propertyNames - Object with the keys as as properties to return,
|
|
605
622
|
* and the values using dot notation to access related records and properties
|
|
606
623
|
* (ex: {parentName: 'somePropertyObject.parent.parent.name', childName: 'child.Name'})
|
|
607
624
|
* @param {FetchObjectOptions} options - {@link module:object~FetchObjectOptions|See FetchObjectOptions}
|
|
608
625
|
* @returns {Object[]} - objects with the properties resolved
|
|
609
626
|
* (ex: {parentname, childName, etc.})
|
|
627
|
+
* @see {@link module:object.fetchObjectProperty|object.fetchObjectProperty} - to safely fetch a single value
|
|
628
|
+
* @see {@link module:object.applyPropertyValues|object.applyPropertyValues} - to safely and deeply apply the list of values extracted to a list of objects.
|
|
629
|
+
* @see {@link module:object.extractObjectProperties|object.extractObjectProperties} - to extract into array vectors instead of objects
|
|
610
630
|
*/
|
|
611
631
|
module.exports.fetchObjectProperties = function fetchObjectProperties(list, propertyNames, options = {}) {
|
|
612
632
|
if (!list) return [];
|
|
@@ -630,11 +650,41 @@ module.exports.fetchObjectProperties = function fetchObjectProperties(list, prop
|
|
|
630
650
|
|
|
631
651
|
/**
|
|
632
652
|
* Accesses a property using a string
|
|
653
|
+
*
|
|
654
|
+
* ```
|
|
655
|
+
* testObj = {
|
|
656
|
+
* name: 'john',
|
|
657
|
+
* courses: [{ name: 'econ-101' }]
|
|
658
|
+
* }
|
|
659
|
+
* utils.object.fetchObjectProperty(testObj, 'courses[0].?name');
|
|
660
|
+
* // 'econ-101'
|
|
661
|
+
* ```
|
|
662
|
+
*
|
|
663
|
+
* note that the options allow for safe property access
|
|
664
|
+
*
|
|
665
|
+
* ```
|
|
666
|
+
* testObj = {
|
|
667
|
+
* name: 'john',
|
|
668
|
+
* courses: [{ name: 'econ-101' }]
|
|
669
|
+
* }
|
|
670
|
+
* utils.object.fetchObjectProperty(testObj, 'courses[0].courseId');
|
|
671
|
+
* // throws an error
|
|
672
|
+
*
|
|
673
|
+
* utils.object.fetchObjectProperty(testObj, 'courses[0].?courseId');
|
|
674
|
+
* // null - because of optional condition operator
|
|
675
|
+
*
|
|
676
|
+
* utils.object.fetchObjectProperty(testObj, 'courses[0].courseId', { safeAccess: true });
|
|
677
|
+
* // null - because of the safe access option
|
|
678
|
+
* ```
|
|
679
|
+
*
|
|
633
680
|
* @param {Object} obj - object to access the properties on
|
|
634
681
|
* @param {String} propertyAccess - dot notation for the property to access
|
|
635
682
|
* (ex: `parent.obj.Name`)
|
|
636
683
|
* @param {FetchObjectOptions} options - {@link module:object~FetchObjectOptions|See FetchObjectOptions}
|
|
637
684
|
* @returns {any} - the value accessed at the end ofthe property chain
|
|
685
|
+
* @see {@link module:object.fetchObjectProperties} - to fetch multiple properties at once into objects
|
|
686
|
+
* @see {@link module:object.extractObjectProperty|object.extractObjectProperty} - to safely extract a deep value without options
|
|
687
|
+
* @see {@link module:object.applyPropertyValue|object.applyPropertyValue} - to apply a single value to a single object using dot notation safely
|
|
638
688
|
*/
|
|
639
689
|
module.exports.fetchObjectProperty = function fetchObjectProperty(obj, propertyAccess, options = {}) {
|
|
640
690
|
if (!obj || !propertyAccess) return null;
|
|
@@ -643,8 +693,14 @@ module.exports.fetchObjectProperty = function fetchObjectProperty(obj, propertyA
|
|
|
643
693
|
safeAccess = false
|
|
644
694
|
} = options;
|
|
645
695
|
|
|
646
|
-
|
|
647
|
-
|
|
696
|
+
const cleanPropertyAccess = String(propertyAccess)
|
|
697
|
+
.replace(/\[/g, '.')
|
|
698
|
+
.replace(/\]/g, '.')
|
|
699
|
+
.replace(/[.]+/g, '.')
|
|
700
|
+
.replace(/^[.]+/, '')
|
|
701
|
+
.replace(/[.]$/, '');
|
|
702
|
+
|
|
703
|
+
return cleanPropertyAccess.split('.')
|
|
648
704
|
.reduce((currentVal, prop) => {
|
|
649
705
|
let isElvis = false;
|
|
650
706
|
let cleanProp = prop;
|
|
@@ -661,6 +717,142 @@ module.exports.fetchObjectProperty = function fetchObjectProperty(obj, propertyA
|
|
|
661
717
|
}, obj);
|
|
662
718
|
};
|
|
663
719
|
|
|
720
|
+
/**
|
|
721
|
+
* Applies a target value onto a source object in-place safely - using dot-notation paths.
|
|
722
|
+
*
|
|
723
|
+
* This can be as simple as safely applying a value even if targetObj may be null
|
|
724
|
+
* ```
|
|
725
|
+
* targetObj = { id: 1, city: 'Seattle', month: 'Aug', precip: 0.87 };
|
|
726
|
+
* utils.object.applyPropertyValue(targetObj, 'state', 'WA');
|
|
727
|
+
* // { id: 1, city: 'Seattle', month: 'Aug', precip: 0.87, state: 'WA };
|
|
728
|
+
* ```
|
|
729
|
+
*
|
|
730
|
+
* working with deeply nested objects
|
|
731
|
+
* ```
|
|
732
|
+
* targetObj = { name: 'john smith', class: { name: 'ECON_101', professor: { last_name: 'Winklemeyer' }} };
|
|
733
|
+
* utils.object.applyPropertyValue(targetObj, 'class.professor.first_name', 'René');
|
|
734
|
+
* // { name: 'john smith', class: { name: 'ECON_101', professor: { last_name: 'Winklemeyer', first_name: 'René' }} };
|
|
735
|
+
* ```
|
|
736
|
+
*
|
|
737
|
+
* or safely working with arrays of values
|
|
738
|
+
* ```
|
|
739
|
+
* targetObj = { name: 'john smith', classes: [{ name: 'ECON_101' }] };
|
|
740
|
+
* utils.object.applyPropertyValue(targetObj, 'classes[0].grade', 'A');
|
|
741
|
+
* // { name: 'john smith', classes: [{ name: 'ECON_101', grade: 'A' }] };
|
|
742
|
+
* ```
|
|
743
|
+
*
|
|
744
|
+
* @param {Object} obj - object to apply the value to
|
|
745
|
+
* @param {string} path - dot notation path to set the value, ex: 'geo', or 'states[0].prop'
|
|
746
|
+
* @param {any} value - value to set
|
|
747
|
+
* @returns {Object} - the object the value was applied to
|
|
748
|
+
* @see {@link module:object.applyPropertyValues|object.applyPropertyValues} - to safely and deeply apply the list of values extracted to a list of objects.
|
|
749
|
+
* @see {@link module:object.extractObjectProperty|object.extractObjectProperty} - to safely extract a deep value
|
|
750
|
+
* @see {@link module:object.fetchObjectProperty|object.fetchObjectProperty} - to extract a deep value and optionally throw if not found
|
|
751
|
+
*/
|
|
752
|
+
module.exports.applyPropertyValue = function applyPropertyValue(obj, path, value) {
|
|
753
|
+
// const signature = 'applyPropertyValue(obj, path, value)';
|
|
754
|
+
|
|
755
|
+
if (!obj) return obj;
|
|
756
|
+
if (!path) return obj;
|
|
757
|
+
|
|
758
|
+
const cleanPath = String(path)
|
|
759
|
+
.replace(/\[/g, '.')
|
|
760
|
+
.replace(/\]/g, '.')
|
|
761
|
+
.replace(/[.]+/g, '.')
|
|
762
|
+
.replace(/^[.]+/, '')
|
|
763
|
+
.replace(/[.]$/, '');
|
|
764
|
+
|
|
765
|
+
const splitPath = cleanPath.split('.');
|
|
766
|
+
const terminalIndex = splitPath.length - 1;
|
|
767
|
+
|
|
768
|
+
return splitPath
|
|
769
|
+
.reduce((currentVal, prop, currentIndex) => {
|
|
770
|
+
//-- can no longer occur
|
|
771
|
+
// if (!prop) throw Error(`${signature}:Unable to set value with path:${path}`);
|
|
772
|
+
|
|
773
|
+
const isLeaf = currentIndex === terminalIndex;
|
|
774
|
+
if (isLeaf) {
|
|
775
|
+
// eslint-disable-next-line no-param-reassign
|
|
776
|
+
currentVal[prop] = value;
|
|
777
|
+
// if (value === undefined) {
|
|
778
|
+
// delete currentVal[prop];
|
|
779
|
+
// } else {
|
|
780
|
+
// currentVal[prop] = value;
|
|
781
|
+
// }
|
|
782
|
+
return obj;
|
|
783
|
+
}
|
|
784
|
+
//-- not a leaf
|
|
785
|
+
if (!currentVal[prop]) {
|
|
786
|
+
// eslint-disable-next-line no-param-reassign
|
|
787
|
+
currentVal[prop] = {};
|
|
788
|
+
}
|
|
789
|
+
return currentVal[prop];
|
|
790
|
+
}, obj);
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Opposite of the extractObjectProperty, this takes a value / set of values
|
|
795
|
+
* and applies them along a given path on each of the target objects.
|
|
796
|
+
*
|
|
797
|
+
* for example:
|
|
798
|
+
*
|
|
799
|
+
* ```
|
|
800
|
+
* weather = [{ id: 1, city: 'Seattle', month: 'Aug', precip: 0.87 },
|
|
801
|
+
* { id: 3, city: 'New York', month: 'Apr', precip: 3.94 },
|
|
802
|
+
* { id: 6, city: 'Chicago', month: 'Apr', precip: 3.62 }];
|
|
803
|
+
*
|
|
804
|
+
* cities = utils.object.extractObjectProperty('city');
|
|
805
|
+
* // ['Seattle', 'New York', 'Chicago'];
|
|
806
|
+
*
|
|
807
|
+
* //-- async process to geocode
|
|
808
|
+
* geocodedCities = geocodeCity(cities);
|
|
809
|
+
* // [{ city: 'Seattle', state: 'WA', country: 'USA' },
|
|
810
|
+
* // { city: 'New York', state: 'NY', country: 'USA' },
|
|
811
|
+
* // { city: 'Chicago', state: 'IL', country: 'USA' }]
|
|
812
|
+
*
|
|
813
|
+
* utils.applyPropertyValues(weather, 'geo', geocodedCities);
|
|
814
|
+
* [{ id: 1, city: 'Seattle', month: 'Aug', precip: 0.87, geo: { city: 'Seattle', state: 'WA', country: 'USA' } },
|
|
815
|
+
* { id: 3, city: 'New York', month: 'Apr', precip: 3.94, geo: { city: 'New York', state: 'NY', country: 'USA' } },
|
|
816
|
+
* { id: 6, city: 'Chicago', month: 'Apr', precip: 3.62, geo: { city: 'Chicago', state: 'IL', country: 'USA' } }];
|
|
817
|
+
*
|
|
818
|
+
* Note that traditional [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
|
|
819
|
+
* works best for if you are working with objects completely in memory.
|
|
820
|
+
*
|
|
821
|
+
* But this helps quite a bit if the action of mapping / transforming values
|
|
822
|
+
* needs to be separate from the extraction / application of values back.
|
|
823
|
+
*
|
|
824
|
+
* @param {Object} obj - object to apply the value to
|
|
825
|
+
* @param {string} path - dot notation path to set the value, ex: 'geo', or 'states[0].prop'
|
|
826
|
+
* @param {any} value - the value that should be set at that path.
|
|
827
|
+
* @returns {Object}
|
|
828
|
+
* @see {@link module:object.applyPropertyValue} - to apply a single value to a single object
|
|
829
|
+
* @see {@link module:object.fetchObjectProperties} - to fetch multiple properties at once into objects
|
|
830
|
+
* @see {@link module:object.extractObjectProperties|object.extractObjectProperties} - to extract properties into array vectors instead of objects
|
|
831
|
+
*/
|
|
832
|
+
module.exports.applyPropertyValues = function applyPropertyValues(objectList, path, valueList) {
|
|
833
|
+
// const signature = 'applyPropertyValues(objectList, path, valueList)';
|
|
834
|
+
if (!objectList || !path) {
|
|
835
|
+
//-- do nothing
|
|
836
|
+
return objectList;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const cleanObjectList = Array.isArray(objectList) ? objectList : [objectList];
|
|
840
|
+
const cleanValueList = Array.isArray(valueList) ? valueList : Array(cleanObjectList.length).fill(valueList);
|
|
841
|
+
|
|
842
|
+
// if (cleanObjectList.length !== cleanValueList) throw Error(
|
|
843
|
+
// `${signature}: objectList.length[${cleanObjectList.length}] does not match valueList.length[${cleanValueList.length}]`
|
|
844
|
+
// );
|
|
845
|
+
const minLength = Math.min(cleanObjectList.length, cleanValueList.length);
|
|
846
|
+
|
|
847
|
+
for (let i = 0; i < minLength; i += 1) {
|
|
848
|
+
const obj = cleanObjectList[i];
|
|
849
|
+
const val = cleanValueList[i];
|
|
850
|
+
ObjectUtils.applyPropertyValue(obj, path, val);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
return objectList;
|
|
854
|
+
};
|
|
855
|
+
|
|
664
856
|
/**
|
|
665
857
|
* Translates specific properties to a new value on an object, or collection of objects.
|
|
666
858
|
*
|