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