UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

58 lines (57 loc) 2.46 kB
import { isArray, omitArrayItems, withArrayItems } from "./array.js"; import { reduceItems } from "./iterate.js"; import { requireNumber } from "./number.js"; import { getProps, isObject } from "./object.js"; import { isDefined } from "./undefined.js"; /** Yield the prop updates in an `Updates` object as a set of `Update` objects. */ export function getUpdates(data) { return getProps(data).map(_getUpdate).filter(isDefined); } function _getUpdate([key, value]) { if (value !== undefined) { if (key.startsWith("+=")) return { action: "sum", key: key.slice(2), value: requireNumber(value, undefined, undefined, getUpdates) }; if (key.startsWith("-=")) return { action: "sum", key: key.slice(2), value: 0 - requireNumber(value, undefined, undefined, getUpdates) }; if (key.startsWith("=")) return { action: "set", key: key.slice(1), value }; if (key.startsWith("+[]")) return { action: "with", key: key.slice(3), value: isArray(value) ? value : [value] }; if (key.startsWith("-[]")) return { action: "omit", key: key.slice(3), value: isArray(value) ? value : [value] }; return { action: "set", key, value }; } } /** Update a data object with a set of updates. */ export function updateData(data, updates) { return reduceItems(getUpdates(updates), _updateProp, data); } function _updateProp(obj, update) { return _updatePropDeep(obj, update, update.key.split("."), 0); } function _updatePropDeep(obj, update, keys, i) { const { action, value } = update; const k = keys[i]; if (!k) return obj; // Shouldn't happen. const oldValue = obj[k]; let newValue = oldValue; if (i === keys.length - 1) { // Final key. if (action === "sum") newValue = typeof oldValue === "number" ? oldValue + value : value; else if (action === "set") newValue = value; else if (action === "with") newValue = isArray(oldValue) ? withArrayItems(oldValue, ...value) : value; else if (action === "omit") newValue = isArray(oldValue) ? omitArrayItems(oldValue, ...value) : []; else return action; // Never happens. } else { // Subkeys. newValue = _updatePropDeep(isObject(oldValue) ? oldValue : {}, update, keys, i + 1); } return oldValue === newValue ? obj : { ...obj, [k]: newValue }; }