shelving
Version:
Toolkit for using data in JavaScript.
58 lines (57 loc) • 2.46 kB
JavaScript
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 };
}