Last active
May 29, 2022 22:43
-
-
Save zeusdeux/73745c6c390a93a30579ad4063df2129 to your computer and use it in GitHub Desktop.
Persistent set operation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function assert(cond, msg) { | |
if (!cond) throw new Error(msg) | |
} | |
function doSet(source, pathToSet, newValue) { | |
assert(Array.isArray(pathToSet), 'path being set needs to be given as an array of prop names in order of access') | |
const prop = pathToSet.shift() | |
if (typeof prop === 'undefined') { | |
return source | |
} | |
const sourceIsArray = Array.isArray(source) | |
assert(sourceIsArray ? !Number.isNaN(Number.parseInt(prop)) : true, `path (prop accessed: ${prop}) being set can\'t be non-numeric when source is an array`) | |
const sourceShallowCopy = sourceIsArray ? source.slice(0) : Object.assign({}, source) | |
if (!pathToSet.length) { | |
sourceShallowCopy[prop] = newValue | |
} else { | |
sourceShallowCopy[prop] = doSet(sourceShallowCopy[prop], pathToSet, newValue) | |
} | |
return sourceShallowCopy | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// run in browser console | |
x = { a: { b: 20, c: [1,2,3,{d: 'bruh'}]}, m: {n: {o: {p: 123}}}, i: null} | |
y = doSet(x, [], 'abc') | |
console.assert(x === y) | |
x = { a: { b: 20, c: [1,2,3,{d: 'bruh'}]}, m: {n: {o: {p: 123}}}, i: null} | |
y = doSet(x, ['a', 'b'], 300) | |
console.assert(x !== y, 'source and returned value from doSet are the same') | |
console.assert(x.m === y.m, 'siblings paths were not shared') | |
console.assert(x.i === y.i, 'siblings paths were not shared') | |
console.assert(x.a !== y.a, 'changed property container object in source and returned value from doSet are same') | |
console.assert(x.a.c === y.a.c, 'sibling paths were not shared') | |
x.a.b = null | |
console.assert(y.a.b === 300, 'path set op failed') | |
x = { a: { b: 20, c: [1,2,3,{d: 'bruh'}]}, m: {n: {o: {p: 123}}}, i: null} | |
y = doSet(x, ['j'], { k: 100 }) | |
console.assert(x !== y, 'source and returned value from doSet are the same') | |
console.assert(x.a === y.a, 'siblings paths were not shared') | |
console.assert(x.m === y.m, 'siblings paths were not shared') | |
console.assert(x.j !== y.j, 'path set op failed') | |
x.j = {k: null} | |
console.assert(y.j.k === 100, 'path set op failed') | |
// test set of prop that is null | |
x = { a: { b: 20, c: [1,2,3,{d: 'bruh'}]}, m: {n: {o: {p: 123}}}, i: null} | |
y = doSet(x, ['i'], 200) | |
console.assert(x !== y, 'source and returned value from doSet are the same') | |
console.assert(x.a === y.a, 'siblings paths were not shared') | |
console.assert(x.m === y.m, 'siblings paths were not shared') | |
console.assert(x.i !== y.i, 'path set op failed') | |
delete x.i | |
console.assert(y.i === 200, 'path set op failed') | |
// array existing index set | |
x = { a: { b: 20, c: [1,2,3,{d: 'bruh'}]}, m: {n: {o: {p: 123}}}, i: null} | |
y = doSet(x, ['a','c', 2], x.a.c[3]) | |
console.assert(x !== y, 'source and returned value from doSet are the same') | |
console.assert(x.m === y.m, 'siblings paths were not shared') | |
console.assert(x.i === y.i, 'siblings paths were not shared') | |
console.assert(x.a !== y.a, 'changed property container object in source and returned value from doSet are same') | |
console.assert(x.a.b === y.a.b, 'siblings paths were not shared') | |
console.assert(x.a.c !== y.a.c, 'changed property container object in source and returned value from doSet are same') | |
console.assert(x.a.c[0] === y.a.c[0], 'siblings paths were not shared') | |
console.assert(x.a.c[1] === y.a.c[1], 'siblings paths were not shared') | |
x.a.c[2] = null | |
console.assert(y.a.c[2] === x.a.c[3], 'path set op failed') | |
console.assert(y.a.c.length === x.a.c.length, 'array length changed when setting existing index in array') | |
// array non-existing index set | |
x = { a: { b: 20, c: [1,2,3,{d: 'bruh'}]}, m: {n: {o: {p: 123}}}, i: null} | |
y = doSet(x, ['a','c', 6], x.a.c[3]) | |
console.assert(x !== y, 'source and returned value from doSet are the same') | |
console.assert(x.m === y.m, 'siblings paths were not shared') | |
console.assert(x.i === y.i, 'siblings paths were not shared') | |
console.assert(x.a !== y.a, 'changed property container object in source and returned value from doSet are same') | |
console.assert(x.a.b === y.a.b, 'siblings paths were not shared') | |
console.assert(x.a.c !== y.a.c, 'changed property container object in source and returned value from doSet are same') | |
console.assert(x.a.c[0] === y.a.c[0], 'siblings paths were not shared') | |
console.assert(x.a.c[1] === y.a.c[1], 'siblings paths were not shared') | |
console.assert(!('6' in x), 'prop being set should not exist in source') | |
console.assert(y.a.c[6] === x.a.c[3], 'path set op failed') | |
console.assert(y.a.c.length === 7, 'path set op failed') | |
console.assert(y.a.c.length !== x.a.c.length, 'array length did not change when setting non-existing index in array') | |
// setting a non-existent subtree | |
x = { a: { b: 20, c: [1,2,3,{d: 'bruh'}]}, m: {n: {o: {p: 123}}}, i: null} | |
y = doSet(x, ['j', 'k', 'l'], 400) | |
console.assert(x !== y, 'source and returned value from doSet are the same') | |
console.assert(x.m === y.m, 'siblings paths were not shared') | |
console.assert(x.i === y.i, 'siblings paths were not shared') | |
console.assert('j' in y && !('j' in x), 'path set op failed') | |
console.assert('k' in y.j, 'path set op failed') | |
console.assert('l' in y.j.k, 'path set op failed') | |
console.assert(y.j.k.l === 400, 'path set op failed') | |
// test all non-object sources are co-erced into objects | |
y = doSet(null, ['i'], 500) | |
console.assert(y.i === 500, 'path set op failed') | |
y = doSet(void 0, ['i'], 500) | |
console.assert(y.i === 500, 'path set op failed') | |
y = doSet(true, ['i'], 500) | |
console.assert(y.i === 500, 'path set op failed') | |
y = doSet(false, ['i'], 500) | |
console.assert(y.i === 500, 'path set op failed') | |
y = doSet('asd', ['i'], 500) | |
console.assert(y.i === 500, 'path set op failed') | |
y = doSet(123, ['i'], 500) | |
console.assert(y.i === 500, 'path set op failed') | |
y = doSet(Symbol.for('test'), ['i'], 500) | |
console.assert(y.i === 500, 'path set op failed') | |
y = doSet(1234n, ['i'], 500) | |
console.assert(y.i === 500, 'path set op failed') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment