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:
parent
3899a65167
commit
56636bf5d4
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user