| import * as Equal from "../Equal.js"; | |
| import { format } from "../Formatter.js"; | |
| import { dual, pipe } from "../Function.js"; | |
| import * as Hash from "../Hash.js"; | |
| import { NodeInspectSymbol, toJson } from "../Inspectable.js"; | |
| import * as Option from "../Option.js"; | |
| import { pipeArguments } from "../Pipeable.js"; | |
| import { hasProperty } from "../Predicate.js"; | |
| import * as Result from "../Result.js"; | |
| /** @internal */ | |
| export const TrieTypeId = "~effect/collections/Trie"; | |
| const trieVariance = { | |
| /* c8 ignore next */ | |
| _Value: _ => _ | |
| }; | |
| const TrieProto = { | |
| [TrieTypeId]: trieVariance, | |
| [Symbol.iterator]() { | |
| return new TrieIterator(this, (k, v) => [k, v], () => true); | |
| }, | |
| [Hash.symbol]() { | |
| let hash = Hash.hash(TrieTypeId); | |
| for (const item of this) { | |
| hash ^= pipe(Hash.hash(item[0]), Hash.combine(Hash.hash(item[1]))); | |
| } | |
| return hash; | |
| }, | |
| [Equal.symbol](that) { | |
| if (isTrie(that)) { | |
| const entries = Array.from(that); | |
| return Array.from(this).every((itemSelf, i) => { | |
| const itemThat = entries[i]; | |
| return Equal.equals(itemSelf[0], itemThat[0]) && Equal.equals(itemSelf[1], itemThat[1]); | |
| }); | |
| } | |
| return false; | |
| }, | |
| toString() { | |
| return `Trie(${format(Array.from(this))})`; | |
| }, | |
| toJSON() { | |
| return { | |
| _id: "Trie", | |
| values: toJson(Array.from(this)) | |
| }; | |
| }, | |
| [NodeInspectSymbol]() { | |
| return this.toJSON(); | |
| }, | |
| pipe() { | |
| return pipeArguments(this, arguments); | |
| } | |
| }; | |
| const makeImpl = root => { | |
| const trie = Object.create(TrieProto); | |
| trie._root = root; | |
| trie._count = root?.count ?? 0; | |
| return trie; | |
| }; | |
| class TrieIterator { | |
| stack = []; | |
| trie; | |
| f; | |
| filter; | |
| constructor(trie, f, filter) { | |
| this.trie = trie; | |
| this.f = f; | |
| this.filter = filter; | |
| const root = trie._root !== undefined ? trie._root : undefined; | |
| if (root !== undefined) { | |
| this.stack.push([root, "", false]); | |
| } | |
| } | |
| next() { | |
| while (this.stack.length > 0) { | |
| const [node, keyString, isAdded] = this.stack.pop(); | |
| if (isAdded) { | |
| const value = node.value; | |
| if (value !== undefined) { | |
| const key = keyString + node.key; | |
| if (this.filter(key, value)) { | |
| return { | |
| done: false, | |
| value: this.f(key, value) | |
| }; | |
| } | |
| } | |
| } else { | |
| this.addToStack(node, keyString); | |
| } | |
| } | |
| return { | |
| done: true, | |
| value: undefined | |
| }; | |
| } | |
| addToStack(node, keyString) { | |
| if (node.right !== undefined) { | |
| this.stack.push([node.right, keyString, false]); | |
| } | |
| if (node.mid !== undefined) { | |
| this.stack.push([node.mid, keyString + node.key, false]); | |
| } | |
| this.stack.push([node, keyString, true]); | |
| if (node.left !== undefined) { | |
| this.stack.push([node.left, keyString, false]); | |
| } | |
| } | |
| [Symbol.iterator]() { | |
| return new TrieIterator(this.trie, this.f, this.filter); | |
| } | |
| } | |
| /** @internal */ | |
| export const isTrie = u => hasProperty(u, TrieTypeId); | |
| /** @internal */ | |
| export const empty = () => makeImpl(undefined); | |
| /** @internal */ | |
| export const fromIterable = entries => { | |
| let trie = empty(); | |
| for (const [key, value] of entries) { | |
| trie = insert(trie, key, value); | |
| } | |
| return trie; | |
| }; | |
| /** @internal */ | |
| export const make = (...entries) => { | |
| return fromIterable(entries); | |
| }; | |
| /** @internal */ | |
| export const insert = /*#__PURE__*/dual(3, (self, key, value) => { | |
| if (key.length === 0) return self; | |
| // -1:left | 0:mid | 1:right | |
| const dStack = []; | |
| const nStack = []; | |
| let n = self._root ?? { | |
| key: key[0], | |
| count: 0 | |
| }; | |
| const count = n.count + 1; | |
| let cIndex = 0; | |
| while (cIndex < key.length) { | |
| const c = key[cIndex]; | |
| nStack.push(n); | |
| if (c > n.key) { | |
| dStack.push(1); | |
| if (n.right === undefined) { | |
| n = { | |
| key: c, | |
| count | |
| }; | |
| } else { | |
| n = n.right; | |
| } | |
| } else if (c < n.key) { | |
| dStack.push(-1); | |
| if (n.left === undefined) { | |
| n = { | |
| key: c, | |
| count | |
| }; | |
| } else { | |
| n = n.left; | |
| } | |
| } else { | |
| if (cIndex === key.length - 1) { | |
| n.value = value; | |
| } else if (n.mid === undefined) { | |
| dStack.push(0); | |
| n = { | |
| key: key[cIndex + 1], | |
| count | |
| }; | |
| } else { | |
| dStack.push(0); | |
| n = n.mid; | |
| } | |
| cIndex += 1; | |
| } | |
| } | |
| // Rebuild path to leaf node (Path-copying immutability) | |
| for (let s = nStack.length - 2; s >= 0; --s) { | |
| const n2 = nStack[s]; | |
| const d = dStack[s]; | |
| if (d === -1) { | |
| // left | |
| nStack[s] = { | |
| key: n2.key, | |
| count, | |
| value: n2.value, | |
| left: nStack[s + 1], | |
| mid: n2.mid, | |
| right: n2.right | |
| }; | |
| } else if (d === 1) { | |
| // right | |
| nStack[s] = { | |
| key: n2.key, | |
| count, | |
| value: n2.value, | |
| left: n2.left, | |
| mid: n2.mid, | |
| right: nStack[s + 1] | |
| }; | |
| } else { | |
| // mid | |
| nStack[s] = { | |
| key: n2.key, | |
| count, | |
| value: n2.value, | |
| left: n2.left, | |
| mid: nStack[s + 1], | |
| right: n2.right | |
| }; | |
| } | |
| } | |
| nStack[0].count = count; | |
| return makeImpl(nStack[0]); | |
| }); | |
| /** @internal */ | |
| export const size = self => self._root?.count ?? 0; | |
| /** @internal */ | |
| export const isEmpty = self => size(self) === 0; | |
| /** @internal */ | |
| export const keys = self => new TrieIterator(self, key => key, () => true); | |
| /** @internal */ | |
| export const values = self => new TrieIterator(self, (_, value) => value, () => true); | |
| /** @internal */ | |
| export const entries = self => new TrieIterator(self, (key, value) => [key, value], () => true); | |
| /** @internal */ | |
| export const reduce = /*#__PURE__*/dual(3, (self, zero, f) => { | |
| let accumulator = zero; | |
| for (const entry of self) { | |
| accumulator = f(accumulator, entry[1], entry[0]); | |
| } | |
| return accumulator; | |
| }); | |
| /** @internal */ | |
| export const map = /*#__PURE__*/dual(2, (self, f) => reduce(self, empty(), (trie, value, key) => insert(trie, key, f(value, key)))); | |
| /** @internal */ | |
| export const filter = /*#__PURE__*/dual(2, (self, f) => reduce(self, empty(), (trie, value, key) => f(value, key) ? insert(trie, key, value) : trie)); | |
| /** @internal */ | |
| export const filterMap = /*#__PURE__*/dual(2, (self, f) => reduce(self, empty(), (trie, value, key) => { | |
| const result = f(value, key); | |
| return Result.isSuccess(result) ? insert(trie, key, result.success) : trie; | |
| })); | |
| /** @internal */ | |
| export const compact = self => reduce(self, empty(), (trie, option, key) => Option.isSome(option) ? insert(trie, key, option.value) : trie); | |
| /** @internal */ | |
| export const forEach = /*#__PURE__*/dual(2, (self, f) => reduce(self, void 0, (_, value, key) => f(value, key))); | |
| /** @internal */ | |
| export const keysWithPrefix = /*#__PURE__*/dual(2, (self, prefix) => new TrieIterator(self, key => key, key => key.startsWith(prefix))); | |
| /** @internal */ | |
| export const valuesWithPrefix = /*#__PURE__*/dual(2, (self, prefix) => new TrieIterator(self, (_, value) => value, key => key.startsWith(prefix))); | |
| /** @internal */ | |
| export const entriesWithPrefix = /*#__PURE__*/dual(2, (self, prefix) => new TrieIterator(self, (key, value) => [key, value], key => key.startsWith(prefix))); | |
| /** @internal */ | |
| export const toEntriesWithPrefix = /*#__PURE__*/dual(2, (self, prefix) => Array.from(entriesWithPrefix(self, prefix))); | |
| /** @internal */ | |
| export const get = /*#__PURE__*/dual(2, (self, key) => { | |
| let n = self._root; | |
| if (n === undefined || key.length === 0) return Option.none(); | |
| let cIndex = 0; | |
| while (cIndex < key.length) { | |
| const c = key[cIndex]; | |
| if (c > n.key) { | |
| if (n.right === undefined) { | |
| return Option.none(); | |
| } else { | |
| n = n.right; | |
| } | |
| } else if (c < n.key) { | |
| if (n.left === undefined) { | |
| return Option.none(); | |
| } else { | |
| n = n.left; | |
| } | |
| } else { | |
| if (cIndex === key.length - 1) { | |
| return Option.fromUndefinedOr(n.value); | |
| } else { | |
| if (n.mid === undefined) { | |
| return Option.none(); | |
| } else { | |
| n = n.mid; | |
| cIndex += 1; | |
| } | |
| } | |
| } | |
| } | |
| return Option.none(); | |
| }); | |
| /** @internal */ | |
| export const has = /*#__PURE__*/dual(2, (self, key) => Option.isSome(get(self, key))); | |
| /** @internal */ | |
| export const getUnsafe = /*#__PURE__*/dual(2, (self, key) => { | |
| const element = get(self, key); | |
| if (Option.isNone(element)) { | |
| throw new Error("Expected trie to contain key"); | |
| } | |
| return element.value; | |
| }); | |
| /** @internal */ | |
| export const remove = /*#__PURE__*/dual(2, (self, key) => { | |
| let n = self._root; | |
| if (n === undefined || key.length === 0) return self; | |
| const count = n.count - 1; | |
| // -1:left | 0:mid | 1:right | |
| const dStack = []; | |
| const nStack = []; | |
| let cIndex = 0; | |
| while (cIndex < key.length) { | |
| const c = key[cIndex]; | |
| if (c > n.key) { | |
| if (n.right === undefined) { | |
| return self; | |
| } else { | |
| nStack.push(n); | |
| dStack.push(1); | |
| n = n.right; | |
| } | |
| } else if (c < n.key) { | |
| if (n.left === undefined) { | |
| return self; | |
| } else { | |
| nStack.push(n); | |
| dStack.push(-1); | |
| n = n.left; | |
| } | |
| } else { | |
| if (cIndex === key.length - 1) { | |
| if (n.value !== undefined) { | |
| nStack.push(n); | |
| dStack.push(0); | |
| cIndex += 1; | |
| } else { | |
| return self; | |
| } | |
| } else { | |
| if (n.mid === undefined) { | |
| return self; | |
| } else { | |
| nStack.push(n); | |
| dStack.push(0); | |
| n = n.mid; | |
| cIndex += 1; | |
| } | |
| } | |
| } | |
| } | |
| const removeNode = nStack[nStack.length - 1]; | |
| nStack[nStack.length - 1] = { | |
| key: removeNode.key, | |
| count, | |
| left: removeNode.left, | |
| mid: removeNode.mid, | |
| right: removeNode.right | |
| }; | |
| // Rebuild path to leaf node (Path-copying immutability) | |
| for (let s = nStack.length - 2; s >= 0; --s) { | |
| const n2 = nStack[s]; | |
| const d = dStack[s]; | |
| const child = nStack[s + 1]; | |
| const nc = child.left === undefined && child.mid === undefined && child.right === undefined ? undefined : child; | |
| if (d === -1) { | |
| // left | |
| nStack[s] = { | |
| key: n2.key, | |
| count, | |
| value: n2.value, | |
| left: nc, | |
| mid: n2.mid, | |
| right: n2.right | |
| }; | |
| } else if (d === 1) { | |
| // right | |
| nStack[s] = { | |
| key: n2.key, | |
| count, | |
| value: n2.value, | |
| left: n2.left, | |
| mid: n2.mid, | |
| right: nc | |
| }; | |
| } else { | |
| // mid | |
| nStack[s] = { | |
| key: n2.key, | |
| count, | |
| value: n2.value, | |
| left: n2.left, | |
| mid: nc, | |
| right: n2.right | |
| }; | |
| } | |
| } | |
| nStack[0].count = count; | |
| return makeImpl(nStack[0]); | |
| }); | |
| /** @internal */ | |
| export const removeMany = /*#__PURE__*/dual(2, (self, keys) => { | |
| let trie = self; | |
| for (const key of keys) { | |
| trie = remove(key)(trie); | |
| } | |
| return trie; | |
| }); | |
| /** @internal */ | |
| export const insertMany = /*#__PURE__*/dual(2, (self, iter) => { | |
| let trie = self; | |
| for (const [key, value] of iter) { | |
| trie = insert(key, value)(trie); | |
| } | |
| return trie; | |
| }); | |
| /** @internal */ | |
| export const modify = /*#__PURE__*/dual(3, (self, key, f) => { | |
| let n = self._root; | |
| if (n === undefined || key.length === 0) return self; | |
| // -1:left | 0:mid | 1:right | |
| const dStack = []; | |
| const nStack = []; | |
| let cIndex = 0; | |
| while (cIndex < key.length) { | |
| const c = key[cIndex]; | |
| if (c > n.key) { | |
| if (n.right === undefined) { | |
| return self; | |
| } else { | |
| nStack.push(n); | |
| dStack.push(1); | |
| n = n.right; | |
| } | |
| } else if (c < n.key) { | |
| if (n.left === undefined) { | |
| return self; | |
| } else { | |
| nStack.push(n); | |
| dStack.push(-1); | |
| n = n.left; | |
| } | |
| } else { | |
| if (cIndex === key.length - 1) { | |
| if (n.value !== undefined) { | |
| nStack.push(n); | |
| dStack.push(0); | |
| cIndex += 1; | |
| } else { | |
| return self; | |
| } | |
| } else { | |
| if (n.mid === undefined) { | |
| return self; | |
| } else { | |
| nStack.push(n); | |
| dStack.push(0); | |
| n = n.mid; | |
| cIndex += 1; | |
| } | |
| } | |
| } | |
| } | |
| const updateNode = nStack[nStack.length - 1]; | |
| if (updateNode.value === undefined) { | |
| return self; | |
| } | |
| nStack[nStack.length - 1] = { | |
| key: updateNode.key, | |
| count: updateNode.count, | |
| value: f(updateNode.value), | |
| // Update | |
| left: updateNode.left, | |
| mid: updateNode.mid, | |
| right: updateNode.right | |
| }; | |
| // Rebuild path to leaf node (Path-copying immutability) | |
| for (let s = nStack.length - 2; s >= 0; --s) { | |
| const n2 = nStack[s]; | |
| const d = dStack[s]; | |
| const child = nStack[s + 1]; | |
| if (d === -1) { | |
| // left | |
| nStack[s] = { | |
| key: n2.key, | |
| count: n2.count, | |
| value: n2.value, | |
| left: child, | |
| mid: n2.mid, | |
| right: n2.right | |
| }; | |
| } else if (d === 1) { | |
| // right | |
| nStack[s] = { | |
| key: n2.key, | |
| count: n2.count, | |
| value: n2.value, | |
| left: n2.left, | |
| mid: n2.mid, | |
| right: child | |
| }; | |
| } else { | |
| // mid | |
| nStack[s] = { | |
| key: n2.key, | |
| count: n2.count, | |
| value: n2.value, | |
| left: n2.left, | |
| mid: child, | |
| right: n2.right | |
| }; | |
| } | |
| } | |
| return makeImpl(nStack[0]); | |
| }); | |
| /** @internal */ | |
| export const longestPrefixOf = /*#__PURE__*/dual(2, (self, key) => { | |
| let n = self._root; | |
| if (n === undefined || key.length === 0) return Option.none(); | |
| let longestPrefixNode = Option.none(); | |
| let cIndex = 0; | |
| while (cIndex < key.length) { | |
| const c = key[cIndex]; | |
| if (n.value !== undefined) { | |
| longestPrefixNode = Option.some([key.slice(0, cIndex + 1), n.value]); | |
| } | |
| if (c > n.key) { | |
| if (n.right === undefined) { | |
| break; | |
| } else { | |
| n = n.right; | |
| } | |
| } else if (c < n.key) { | |
| if (n.left === undefined) { | |
| break; | |
| } else { | |
| n = n.left; | |
| } | |
| } else { | |
| if (n.mid === undefined) { | |
| break; | |
| } else { | |
| n = n.mid; | |
| cIndex += 1; | |
| } | |
| } | |
| } | |
| return longestPrefixNode; | |
| }); | |
| //# sourceMappingURL=trie.js.map |
Xet Storage Details
- Size:
- 14.2 kB
- Xet hash:
- 0a4d274b28c781bb1150040d22a822edc101546eb79c4959b48f2ba697577ae4
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.