function copyWith (obj, key, value) { const result = typeof key === 'number' ? [...obj] : {...obj}; result[key] = value; return result; } export const cowValueModel = (...keyDescriptions) => { const keys = []; function fillKeys (keyDescriptions) { keyDescriptions.forEach(each => { if (typeof each === 'number') keys.push(each); else if (Array.isArray(each)) fillKeys(each); else keys.push(...each.toString().split('.')); }); } fillKeys(keyDescriptions); function setField (x, index, val) { if (index >= keys.length) return val; const key = keys[index], value = x == null ? undefined : x[key], modified = setField(value, index + 1, val); return value === modified ? x : copyWith(x, key, modified); } const GET_SENTINEL = {}; return (obj, val = GET_SENTINEL) => val === GET_SENTINEL ? keys.reduce((x, key) => x == null ? undefined : x[key], obj) : setField(obj, 0, val); }; export const cowWorkshop = (keys, fn = x => x) => (obj, {result = obj, resultKeys = keys, diff} = {}) => { keys.forEach((key, index) => { const value = fn(cowValueModel(key)(obj)); if (typeof value === "undefined") return; const modifier = cowValueModel(resultKeys[index]); const oldResult = result; result = modifier(oldResult, value); if (result !== oldResult) { diff = modifier(diff, value); } }); return {result, diff}; };