chore(xo-server/MultiKeyMap): better implementation (#4070)

No longer unnecessarily use a map per entry and avoid creating Node as much as possible.
This commit is contained in:
Julien Fontanet 2019-03-26 17:50:22 +01:00 committed by Pierre Donias
parent 3899a65167
commit 56636bf5d4

View File

@ -1,58 +1,87 @@
const kValue = Symbol('value') class Node {
constructor(value) {
this.children = new Map()
this.value = value
}
}
function del(map, i, n, keys) { function del(node, i, keys) {
if (i === n) { if (i === keys.length) {
return map.delete(kValue) if (node instanceof Node) {
node.value = undefined
return node
}
return
}
if (!(node instanceof Node)) {
return node
} }
const key = keys[i] const key = keys[i]
const child = map.get(key) const { children } = node
const child = children.get(key)
if (child === undefined) { if (child === undefined) {
return false return node
} }
const deleted = del(child, i + 1, n, keys) const newChild = del(child, i + 1, keys)
if (child.size === 0) { if (newChild === undefined) {
map.delete(key) if (children.size === 1) {
return node.value
} }
return deleted children.delete(key)
} else if (newChild !== child) {
children.set(key, newChild)
}
return node
} }
function get(map, i, n, keys) { function get(node, i, keys) {
if (i === n) { return i === keys.length
return map.get(kValue) ? node instanceof Node
} ? node.value
const child = map.get(keys[i]) : node
if (child !== undefined) { : node instanceof Node
return get(child, i + 1, n, keys) ? get(node.children.get(keys[i]), i + 1, keys)
} : undefined
} }
function set(map, i, n, keys, value) { function set(node, i, keys, value) {
if (i === n) { if (i === keys.length) {
return map.set(kValue, value) if (node instanceof Node) {
node.value = value
return node
}
return value
} }
const key = keys[i] const key = keys[i]
let child = map.get(key) if (!(node instanceof Node)) {
if (child === undefined) { node = new Node(node)
map.set(key, (child = new Map())) node.children.set(key, set(undefined, i + 1, keys, value))
} else {
const { children } = node
const child = children.get(key)
const newChild = set(child, i + 1, keys, value)
if (newChild !== child) {
children.set(key, newChild)
} }
set(child, i + 1, n, keys, value) }
return node
} }
export default class MultiKeyMap { export default class MultiKeyMap {
constructor() { constructor() {
this._map = new Map() // each node is either a value or a Node if it contains children
this._root = undefined
} }
delete(keys) { delete(keys) {
return del(this._map, 0, keys.length, keys) this._root = del(this._root, 0, keys)
} }
get(keys) { get(keys) {
return get(this._map, 0, keys.length, keys) return get(this._root, 0, keys)
} }
set(keys, value) { set(keys, value) {
set(this._map, 0, keys.length, keys, value) this._root = set(this._root, 0, keys, value)
return this
} }
} }