Skip to content

Instantly share code, notes, and snippets.

@zeusdeux
Last active May 29, 2022 22:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeusdeux/73745c6c390a93a30579ad4063df2129 to your computer and use it in GitHub Desktop.
Save zeusdeux/73745c6c390a93a30579ad4063df2129 to your computer and use it in GitHub Desktop.
Persistent set operation
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
}
// 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