UNPKG

@rimbu/deep

Version:

Tools to use handle plain JS objects as immutable objects

108 lines 3.84 kB
import { Deep } from './internal.mjs'; export var Path; (function (Path) { /** * Regular expression used to split a path string into tokens. */ Path.stringSplitRegex = /\?\.|\.|\[|\]/g; /** * Return the given `path` string split into an array of subpaths. * @param path - the input string path */ function stringSplit(path) { return path.split(Path.stringSplitRegex); } Path.stringSplit = stringSplit; })(Path || (Path = {})); /** * Returns the value resulting from selecting the given `path` in the given `source` object. * It supports optional chaining for nullable values or values that may be undefined, and also * for accessing objects inside an array. * There is currently no support for forcing non-null (the `!` operator). * @typeparam T - the object type to select in * @typeparam P - a Path in object type T * @param source - the object to select in * @param path - the path into the object * @example * ```ts * const value = { a: { b: { c: [{ d: 5 }, { d: 6 }] } } } * Deep.getAt(value, 'a.b'); * // => { c: [{ d: 5 }, { d: 6 }] } * Deep.getAt(value, 'a.b.c'); * // => [{ d: 5 }, { d: 6 }] * Deep.getAt(value, 'a.b.c[1]'); * // => { d: 6 } * Deep.getAt(value, 'a.b.c[1]?.d'); * // => 6 * ``` */ export function getAt(source, path) { if (path === '') { // empty path always directly returns source value return source; } const items = Path.stringSplit(path); // start with `source` as result value let result = source; for (const item of items) { if (undefined === item || item === '' || item === '[') { // ignore irrelevant items continue; } if (undefined === result || null === result) { // optional chaining assumed and no value available, skip rest of path and return undefined return undefined; } // set current result to subpath value result = result[item]; } return result; } /** * Patches the value at the given path in the source to the given value. * Because the path to update must exist in the `source` object, optional * chaining and array indexing is not allowed. * @param source - the object to update * @param path - the path in the object to update * @param patchItem - the patch for the value at the given path * @example * ```ts * const value = { a: { b: { c: 5 } } }; * Deep.patchAt(value, 'a.b.c', v => v + 5); * // => { a: { b: { c: 6 } } } * ``` */ export function patchAt(source, path, patchItem) { if (path === '') { return Deep.patch(source, patchItem); } const items = Path.stringSplit(path); // creates a patch object based on the current path function createPatchPart(index, target) { if (index === items.length) { // processed all items, return the input `patchItem` return patchItem; } const item = items[index]; if (undefined === item || item === '') { // empty items can be ignored return createPatchPart(index + 1, target); } if (item === '[') { // next item is array index, set arrayMode to true return createPatchPart(index + 1, target); } // create object with subPart as property key, and the restuls of processing next parts as value const result = { [item]: createPatchPart(index + 1, target[item]), }; if (Array.isArray(target)) { // target in source object is array/tuple, so the patch should be object return result; } // target in source is not an array, so it patch should be an array return [result]; } return Deep.patch(source, createPatchPart(0, source)); } //# sourceMappingURL=path.mjs.map