-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented tree iterator for quads query
- Loading branch information
1 parent
673bb5d
commit 0b9fe96
Showing
4 changed files
with
248 additions
and
28 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,123 @@ | ||
import { AsyncIterator } from "https://esm.sh/[email protected]"; | ||
|
||
export { | ||
ArrayIterator, | ||
type AsyncIterator, | ||
MappingIterator, | ||
} from "https://esm.sh/[email protected]"; | ||
|
||
type TreeNode<T> = { | ||
[property: string]: T[] | TreeNode<T>; | ||
}; | ||
|
||
export type Tree<T> = T[] | TreeNode<T>; | ||
|
||
type Subtree<T> = { | ||
type: "node"; | ||
tree: TreeNode<T>; | ||
parent?: Subtree<T>; | ||
items: string[]; | ||
path: string[]; | ||
index: number; | ||
} | { | ||
type: "leaf"; | ||
parent?: Subtree<T>; | ||
items: T[]; | ||
path: string[]; | ||
index: number; | ||
}; | ||
|
||
export class TreeIterator<T> extends AsyncIterator<[...string[], T]> { | ||
private _tree: Tree<T>; | ||
private _pointer: Subtree<T>; | ||
|
||
constructor(tree: Tree<T>) { | ||
super(); | ||
this._tree = tree; | ||
this._pointer = this._buildPointerFromSource(tree); | ||
this.readable = true; | ||
} | ||
|
||
read() { | ||
if (this.closed) { | ||
return null; | ||
} | ||
|
||
this._findNextCommonSegment(); | ||
this._findNextLeaf(); | ||
|
||
const p = this._pointer; | ||
if (p.type === "leaf") { | ||
p.index++; | ||
if (p.index < p.items.length) { | ||
return [...p.path, p.items[p.index]] as [...string[], T]; | ||
} | ||
if (!p.parent) { | ||
this.close(); | ||
} | ||
} | ||
if (p.type === "node") { | ||
this.close(); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
protected _buildPointerFromSource( | ||
tree: Tree<T>, | ||
path: string[] = [], | ||
): Subtree<T> { | ||
if (tree.constructor === Object && !Array.isArray(tree)) { | ||
return { | ||
tree, | ||
parent: this._pointer, | ||
type: "node", | ||
items: Object.keys(tree), | ||
path, | ||
index: -1, | ||
}; | ||
} | ||
if (Array.isArray(tree)) { | ||
return { | ||
parent: this._pointer, | ||
type: "leaf", | ||
items: tree, | ||
path, | ||
index: -1, | ||
}; | ||
} else { | ||
throw new Error( | ||
"Invalid tree specified, expecting arrays in plain objects.", | ||
); | ||
} | ||
} | ||
|
||
protected _findNextCommonSegment() { | ||
while (this._pointer.parent !== null) { | ||
const p = this._pointer; | ||
if (p.type === "leaf" && p.index < p.items.length - 1) { | ||
// Points to a leaf that is not yet exhausted | ||
break; | ||
} | ||
if (p.index >= p.items.length - 1 && this._pointer.parent) { | ||
this._pointer = this._pointer.parent!; | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
|
||
protected _findNextLeaf() { | ||
while (this._pointer.type !== "leaf") { | ||
const p = this._pointer; | ||
p.index++; | ||
if (p.index >= p.items.length) { | ||
// no other keys present, the tree is exhausted; | ||
break; | ||
} | ||
const key = p.items[p.index]; | ||
const source = this._pointer.tree[key]; | ||
this._pointer = this._buildPointerFromSource(source, [...p.path, key]); | ||
} | ||
} | ||
} |
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { assertEquals } from "./test_deps.ts"; | ||
import { type Tree, TreeIterator } from "../library/asynciterator.ts"; | ||
|
||
async function assertIterator(input: Tree<unknown>, output: unknown[]) { | ||
const i = new TreeIterator(input); | ||
const result = await i.toArray(); | ||
console.log("RESULT", result); | ||
assertEquals(result, output); | ||
} | ||
|
||
Deno.test("TreeIterator / Empty object", async () => { | ||
const input = {}; | ||
const output: unknown[] = []; | ||
await assertIterator(input, output); | ||
}); | ||
|
||
Deno.test("TreeIterator / Simple array", async () => { | ||
const input = ["hello", "world"]; | ||
const output = [["hello"], ["world"]]; | ||
await assertIterator(input, output); | ||
}); | ||
|
||
Deno.test("TreeIterator / One level tree", async () => { | ||
const input = { | ||
key: ["hello", "world"], | ||
other: ["hello", "world", "!"], | ||
}; | ||
const output = [ | ||
["key", "hello"], | ||
["key", "world"], | ||
["other", "hello"], | ||
["other", "world"], | ||
["other", "!"], | ||
]; | ||
await assertIterator(input, output); | ||
}); | ||
|
||
Deno.test("TreeIterator / Two level tree", async () => { | ||
const input = { | ||
one: { | ||
two: ["hello", "world"], | ||
other: ["!"], | ||
}, | ||
}; | ||
const output = [ | ||
["one", "two", "hello"], | ||
["one", "two", "world"], | ||
["one", "other", "!"], | ||
]; | ||
await assertIterator(input, output); | ||
}); | ||
|
||
Deno.test("TreeIterator / Asymetric tree", async () => { | ||
const input = { | ||
a: { | ||
aa: ["aaa", "aab"], | ||
ab: { | ||
aba: ["!", "!"], | ||
}, | ||
}, | ||
b: ["bb"], | ||
}; | ||
const output = [ | ||
["a", "aa", "aaa"], | ||
["a", "aa", "aab"], | ||
["a", "ab", "aba", "!"], | ||
["a", "ab", "aba", "!"], | ||
["b", "bb"], | ||
]; | ||
await assertIterator(input, output); | ||
}); |