UNPKG

locutus

Version:

Locutus other languages' standard libraries to JavaScript for fun and educational purposes

60 lines (59 loc) 2.25 kB
function isRecordLike(value) { return typeof value === 'object' && value !== null; } function cloneForKey(value, nextKey) { if (Array.isArray(value)) { return value.slice(); } if (isRecordLike(value)) { return { ...value }; } return typeof nextKey === 'number' ? [] : {}; } export function update_in(value, keys, updater, ...args) { // discuss at: https://locutus.io/clojure/update_in/ // parity verified: Clojure 1.12 // original by: Kevin van Zonneveld (https://kvz.io) // note 1: Mirrors Clojure update-in by immutably updating a nested path and creating missing containers. // example 1: update_in({a: {b: 1}}, ['a', 'b'], (n) => Number(n) + 1) // returns 1: {a: {b: 2}} // example 2: update_in({a: {}}, ['a', 'b'], (v) => (v === undefined ? 10 : Number(v))) // returns 2: {a: {b: 10}} // example 3: update_in([1, {x: 3}], [1, 'x'], (n) => Number(n) * 2) // returns 3: [1, {x: 6}] if (typeof updater !== 'function') { throw new TypeError('update_in(): updater must be a function'); } if (!Array.isArray(keys)) { return updater(value, ...args); } if (keys.length === 0) { return updater(value, ...args); } const root = cloneForKey(value, keys[0]); let sourceCursor = value; let targetCursor = root; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; const nextKey = keys[i + 1]; if (key === undefined || nextKey === undefined) { continue; } const sourceNext = sourceCursor !== null && typeof sourceCursor === 'object' ? sourceCursor[key] : undefined; const clonedNext = cloneForKey(sourceNext, nextKey); targetCursor[key] = clonedNext; sourceCursor = sourceNext; targetCursor = clonedNext; } const leafKey = keys[keys.length - 1]; if (leafKey === undefined) { return root; } const currentLeafValue = sourceCursor !== null && typeof sourceCursor === 'object' ? sourceCursor[leafKey] : undefined; targetCursor[leafKey] = updater(currentLeafValue, ...args); return root; }