Skip to content

Instantly share code, notes, and snippets.

@chase-moskal
Last active October 8, 2022 01:22
Show Gist options
  • Save chase-moskal/695b60487da96e606edb5cea57ee8ba0 to your computer and use it in GitHub Desktop.
Save chase-moskal/695b60487da96e606edb5cea57ee8ba0 to your computer and use it in GitHub Desktop.
typescript mappable tree node class
type Unpacked<T> =
T extends (infer U)[]
? U
: T extends (...args: any[]) => infer U
? U
: T extends Promise<infer U>
? U
: T
function arrayFlatten<Type>(array: any[]): Type[] {
return array.reduce(
(arr, value) => Array.isArray(value)
? arr.concat(arrayFlatten(value))
: arr.concat(value), []
)
}
export class TreeNode<T> {
readonly value: T
readonly container: boolean
readonly children: TreeNode<T>[] = []
constructor(value: T, container: boolean = false) {
this.value = value
this.container = container
}
addChildValue(value: T) {
const child = new TreeNode(value)
this.children.push(child)
}
addChildNode(node: TreeNode<T>) {
this.children.push(node)
}
toArray(): T[] {
const children = arrayFlatten<T>(this.children.map(child => child.toArray()))
return this.container
? children
: [this.value, ...children]
}
map<T2>(callback: (value: T) => T2): TreeNode<T2> {
const newTree = this.container
? new TreeNode<T2>(undefined, this.container)
: new TreeNode<T2>(callback(this.value), this.container)
const children = this.children.map(child => child.map(callback))
for (const child of children) newTree.addChildNode(child)
return newTree
}
filter(callback: (value: T) => boolean): TreeNode<T> {
const newTree = new TreeNode<T>(this.value, this.container)
const filteredChildren = this.children
.filter(child => callback(<T>child.value))
.map(child => child.filter(callback))
for (const child of filteredChildren) newTree.addChildNode(child)
return newTree
}
async promiseAll(): Promise<TreeNode<Unpacked<T>>> {
if (this.container) {
const newChildren = await Promise.all(
this.children.map(child => child.promiseAll())
)
const newTree = new TreeNode(undefined, this.container)
for (const newChild of newChildren) newTree.addChildNode(newChild)
return newTree
}
else {
const [newValue, newChildren] = await Promise.all([
this.value,
Promise.all(this.children.map(child => child.promiseAll()))
])
const newTree = new TreeNode<Unpacked<T>>(<any>newValue, this.container)
for (const newChild of newChildren) newTree.addChildNode(newChild)
return newTree
}
}
}
@SidiqovAbbos
Copy link

That's looking really cool!👍
What about parent node? Can we include that too?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment