🐕

フラットな配列から木構造を作る

2021/05/11に公開

https://twitter.com/suin/status/1390626013814476800

const input = [
  { level: 1, text: '1章' },
  { level: 1, text: '2章' },
  { level: 2, text: '2.1' },
  { level: 2, text: '2.2' },
  { level: 1, text: '第3章' },
  { level: 2, text: '3.1' },
  { level: 2, text: '3.2' },
  { level: 3, text: '3.2.1' },
  { level: 3, text: '3.2.2' },
]
type TreeNode<I> = Omit<I, 'children'> & { children?: TreeNode<I>[] }
type Input<I> = (I & { children: unknown })[]
const treefy = <I extends { level: number }>(input: I[]) => {
  const tree: TreeNode<I>[] = []
  const last: TreeNode<I>[] = []
  let top = -1
  let bottom = -1
  for (const { children: _, ...item } of input as Input<I>) {
    if (top < item.level && item.level <= bottom + 1) {
      const idx = item.level - top
      bottom = item.level
      ;(last[idx - 1].children ??= []).push(item)
      last[idx] = item
      continue
    }
    tree.push(item)
    last[0] = item
    top = bottom = item.level
  }
  return tree
}
const output = treefy(input)
console.log(JSON.stringify(output, null, 2))

TS Playground

解説

for 文のネストや array#pop などを使用しないことで軽量になっていると思います。
level が一度に 2 以上増えないという前提で書いているのでそれが壊れるとこれも壊れます。

GitHubで編集を提案

Discussion