跪拜 Guibai
← Back to the summary

10 Lines of Code: The Flat-to-Tree Algorithm Every Frontend Dev Needs

Written in front: Today I learned a super common algorithm in real work — list to tree. The backend queries select * from menu from the database and gets a one-dimensional flat list, but the frontend needs to display a tree structure (multi-level menus, address selectors, organizational charts...). How to convert? The teacher taught two tricks: the Map method and the reduce method. After listening, I just want to say: it turns out to be so simple, and I used to write recursion by hand for a long time!


I. Why do we need "list to tree"?

1.1 The data the backend gives looks like this

The teacher gave a very realistic example:

const flatList = [
    { id: 1, name: 'Level 1 Menu A', parentId: 0 },
    { id: 2, name: 'Level 1 Menu B', parentId: 0 },
    { id: 3, name: 'Level 2 A-1', parentId: 1 },
    { id: 4, name: 'Level 3 A-1-1', parentId: 3 },
    { id: 5, name: 'Level 2 B-1', parentId: 2 },
]

This is a bunch of flat data — all items are in the same array, with no nesting relationship. Each item has a parentId, indicating who its parent node is.

1.2 The data the frontend wants looks like this

But frontend components (like Element UI's Tree, Ant Design's Cascader) need a tree structure:

[
  {
    "id": 1,
    "name": "Level 1 Menu A",
    "parentId": 0,
    "children": [
      {
        "id": 3,
        "name": "Level 2 A-1",
        "parentId": 1,
        "children": [
          {
            "id": 4,
            "name": "Level 3 A-1-1",
            "parentId": 3,
            "children": []
          }
        ]
      }
    ]
  },
  {
    "id": 2,
    "name": "Level 1 Menu B",
    "parentId": 0,
    "children": [
      {
        "id": 5,
        "name": "Level 2 B-1",
        "parentId": 2,
        "children": []
      }
    ]
  }
]

From flat to tree — this is the problem "list to tree" solves.

The teacher mentioned that this requirement is especially common in admin backends:

MySQL stores flat structures; select * from returns a one-dimensional array. So list-to-tree is an almost mandatory data processing task in front-end/back-end separation projects.


II. Method 1: Map method — space for time

2.1 Core idea

The first method the teacher taught uses the ES6 Map data structure:

"ES6 new data structure HashMap"

Core idea: First store all nodes in a Map (id → node), then traverse once more, attaching each node under its corresponding parent node.

2.2 Code implementation

function listToTree(list) {
    const map = new Map(); // HashMap, id -> node
    const tree = [];

    // First pass: put all nodes into the Map, and initialize children
    list.forEach((item) => {
        map.set(item.id, {
            ...item,        // spread original properties
            children: []    // add an empty array
        });
    });

    // Second pass: attach each node under its parent node
    list.forEach((item) => {
        const current = map.get(item.id);       // current item
        const parent = map.get(item.parentId);  // parent node of current item
        if (parent) {
            parent.children.push(current); // has parent, attach
        } else {
            tree.push(current); // no parent, it's a root node
        }
    });

    return tree;
}

2.3 Execution process breakdown

Using the example data, see how the code runs:

First pass: Build the Map

Map {
  1 → { id: 1, name: 'Level 1 Menu A', parentId: 0, children: [] }
  2 → { id: 2, name: 'Level 1 Menu B', parentId: 0, children: [] }
  3 → { id: 3, name: 'Level 2 A-1', parentId: 1, children: [] }
  4 → { id: 4, name: 'Level 3 A-1-1', parentId: 3, children: [] }
  5 → { id: 5, name: 'Level 2 B-1', parentId: 2, children: [] }
}

Second pass: Attach to parent

Current item parentId Parent exists? Operation
id: 1 0 map.get(0) = undefined tree.push(node1)
id: 2 0 map.get(0) = undefined tree.push(node2)
id: 3 1 map.get(1) = node1 node1.children.push(node3)
id: 4 3 map.get(3) = node3 node3.children.push(node4)
id: 5 2 map.get(2) = node2 node2.children.push(node5)

Final result:

tree = [node1, node2]
node1.children = [node3]
node3.children = [node4]
node2.children = [node5]

2.4 Time complexity: O(n)

Two traversals, each O(n), total time complexity O(n). Uses a Map for space-for-time, parent lookup is O(1).

It's like organizing a family tree:

  1. First line everyone up by their ID number (id) in a Map.
  2. Then ask each one "Who is your father?" (parentId), and stand behind the father (children.push).

III. Method 2: reduce method — elegance of functional programming

3.1 Core idea

The second method the teacher taught uses reduce, more functional and concise:

function listToTree(list) {
    // First reduce: build nodeMap
    const nodeMap = list.reduce((map, item) => {
        map[item.id] = { ...item, children: [] };
        return map;
    }, {});

    // Second reduce: assemble tree
    return list.reduce((tree, item) => {
        const cur = nodeMap[item.id];
        const parent = nodeMap[item.parentId];
        if (parent) {
            parent.children.push(cur);
        } else {
            tree.push(cur);
        }
        return tree;
    }, []);
}

3.2 The magic of reduce

reduce is the array's "universal folder" — it folds an array into a single value.

First reduce:

Second reduce:

3.3 Map vs reduce: two styles, same efficiency

Comparison Map method reduce method
Code style Imperative (forEach) Functional (reduce)
Readability Intuitive, clear steps Concise, one operation per line
Time complexity O(n) O(n)
Space complexity O(n) (Map) O(n) (object)
Applicable scenario Team prefers imperative Team prefers functional

The core idea of both methods is exactly the same: first build an index, then attach parent-child relationships. Only the implementation differs.


IV. Key knowledge point: parentId is the "DNA" of the tree

4.1 Why is parentId so important?

The teacher emphasized:

"parentId is the key to the tree."

In databases, storing tree structures usually has two schemes:

Scheme Storage method Advantages Disadvantages
Adjacency List Each node stores parentId Simple and intuitive, easy insert/delete Querying subtrees requires recursion
Nested Set Stores left and right Fast subtree queries Insert/delete is troublesome

parentId is the core of the adjacency list scheme. Most admin backends use this scheme because it's simple, intuitive, and easy to maintain.

4.2 Flat vs tree: two perspectives

Flat perspective (database):          Tree perspective (frontend):
                              
[id:1, parentId:0]                   Level 1 Menu A
[id:2, parentId:0]                   ├── Level 2 A-1
[id:3, parentId:1]                   │   └── Level 3 A-1-1
[id:4, parentId:3]                   └── Level 2 B-1
[id:5, parentId:2]                   

Databases like flat (fast queries, saves storage), frontends like tree (intuitive display, easy recursion). List-to-tree is the conversion between these two perspectives.


V. The magic of JSON.stringify: printing tree structures

The teacher also showed a debugging trick:

console.log(JSON.stringify(listToTree(flatList), null, 2))

JSON.stringify(obj, null, 2) can format an object into a string with indentation.

This makes the tree structure very clear in the console, much better than directly console.log a bunch of [object Object].

It's like writing an article:


VI. Summary: List to tree, a "basic skill" every frontend must master

Knowledge point Explanation
List to tree Convert flat array to nested tree structure
parentId Key field identifying parent node
Map method Use HashMap as index, two traversals
reduce method Use reduce to fold array, functional style
Time complexity O(n), linear traversal
Application scenarios Multi-level menus, address selection, organizational charts
JSON.stringify(obj, null, 2) Format and print objects

List to tree is a high-frequency frontend interview question and a skill used daily in real work. Mastering the Map method and reduce method, no matter which style the interviewer asks, you can handle it calmly.


Written at the end

Today's biggest gain is understanding the idea of "space for time." Using Map as an index optimizes O(n²) nested lookup into O(n) linear traversal — this idea can be used in many algorithm problems.

Next time an interviewer asks you: "How to convert a flat array into a tree structure?"

You can calmly say:

"I use Map as an index, two traversals. First pass: put all nodes into a Map (id → node) and initialize the children array. Second pass: according to parentId, find the parent node, push the current node into the parent's children. If parentId cannot find a corresponding node, it's a root node, push directly into the result array. Time complexity O(n). Can also use reduce for a more functional style."

Then watch the interviewer's satisfied expression, and silently think: This round, I'm safe again.


All code examples in this article come from classroom learning materials, real and runnable.