Last active
May 14, 2017 17:52
-
-
Save FireNeslo/f045db7b8897bfebd364efd29af2f9da to your computer and use it in GitHub Desktop.
Immutable thingy
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
var {ARRAY, NUMBER, STRING, BOOLEAN, OBJECT} = { | |
ARRAY: Symbol('array'), | |
OBJECT: Symbol('object'), | |
NUMBER: Symbol('number'), | |
STRING: Symbol('string'), | |
BOOLEAN: Symbol('boolean'), | |
} | |
function flatMap(list, callback) { | |
const result = [] | |
for(const [key, value] of Object.entries(list)) { | |
result.push(...callback(value, key)) | |
} | |
return result | |
} | |
function hash(value) { | |
switch(typeof value) { | |
case 'number': return [NUMBER, ...value.toString(5)] | |
case 'string': return [STRING, ...value] | |
case 'boolean': return [BOOLEAN, value] | |
} | |
if(Array.isArray(value)) { | |
return [ARRAY, ...flatMap(value, hash)] | |
} | |
return [OBJECT, ...flatMap(value, (value, key)=>flatMap([key, value], hash))] | |
} | |
var Node = class Node { | |
constructor(values, nodes=null) { | |
this.nodes = Object.assign({}, nodes) | |
this.value = null | |
this.key = null | |
if(values) { | |
for(var [key, value] of Object.entries(values)) { | |
this.mutateSet(key, value) | |
} | |
} | |
} | |
*[Symbol.iterator]() { | |
for(let index of Reflect.ownKeys(this.nodes)) { | |
const node = this.nodes[index] | |
if(node.key !== null) { | |
yield [node.key, node.value] | |
} else { | |
yield* node | |
} | |
} | |
} | |
mutateSet(key, value) { | |
const path = hash(key) | |
let node = this | |
for(var index of path) { | |
node = node.nodes[index] || (node.nodes[index] = new Node()) | |
} | |
node.key = key | |
node.value = value | |
return this | |
} | |
delete(key) { | |
const path = hash(key) | |
const root = new Node(null, this.nodes) | |
const last = path.length - 1 | |
let node = root | |
let from = this | |
for(var i = 0; i <= last; i++) { | |
const index = path[i] | |
if(!(from = from && from.nodes[index])) return this | |
node = node.nodes[index] = new Node(null, from.nodes) | |
} | |
delete node.nodes[path[last]] | |
return root | |
} | |
set(key, value) { | |
const path = hash(key) | |
const root = new Node(null, this.nodes) | |
let node = root | |
let from = this | |
for(var index of path) { | |
from = from && from.nodes[index] | |
node = node.nodes[index] = new Node(null, from && from.nodes) | |
} | |
node.value = value | |
node.key = key | |
return root | |
} | |
get(key) { | |
const path = hash(key) | |
let node = this | |
for(var index of path) { | |
if(!node.nodes[index]) break | |
node = node.nodes[index] | |
} | |
return node.value | |
} | |
} | |
function Map(values) { | |
return new Node(values) | |
} | |
var node = Map({a: 6, b: {}}) | |
var before = node.set({a: 1}, 5) | |
var after = before.delete({a: 1}) | |
console.log('nonexistent: ', before.get({a: 2})) | |
console.log('before: ', before.get({a: 1})) | |
console.log('after: ', after.get({a: 1})) | |
for(let [key, value] of node) { | |
console.log({key, value}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment