# Bug Report: `unflatten` function calculates `NodeLevel` incorrectly when input data is not sorted by hierarchy
## Summary
The `unflatten` function produces incorrect `NodeLevel` values when the input array is not sorted in hierarchical order (i.e., parent nodes do not necessarily appear before their children).
## Root Cause
`NodeLevel` is calculated during the parent-child relationship building loop. This means the calculation depends on the parent's `NodeLevel` already being set. When a child node appears before its parent in the input array, the parent's `NodeLevel` is still `undefined`.
```js
d = h[r.ParentNodeId].NodeLevel;
r.NodeLevel = (void 0 === d ? "" : d) + 1;
```
This causes two problems:
1. **`undefined` fallback to empty string `""`**: When the parent's `NodeLevel` is `undefined`, it falls back to `""`, and `"" + 1` results in `1` regardless of the actual depth.
2. **String concatenation instead of numeric addition**: Once `NodeLevel` becomes the string `"1"`, subsequent calculations produce `"1" + 1 = "11"`, `"11" + 1 = "111"`, etc.
## Steps to Reproduce
Provide input data where a child node appears before its parent:
```json
[
{ "NodeId": "A", "ParentNodeId": "0" },
{ "NodeId": "C", "ParentNodeId": "B" },
{ "NodeId": "B", "ParentNodeId": "A" },
{ "NodeId": "D", "ParentNodeId": "C" },
{ "NodeId": "E", "ParentNodeId": "D" }
]
## Expected vs Actual
| NodeId | Expected NodeLevel | Actual NodeLevel |
|--------|-------------------|-----------------|
| A | 0 | *(not set)* |
| B | 1 | 1 |
| C | 2 | 1 |
| D | 3 | "11" |
| E | 4 | "111" |
## Proposed Fix
Separate the `NodeLevel` calculation from the parent-child relationship building loop. After the tree structure is fully constructed, recursively assign `NodeLevel` starting from the root nodes.
function unflatten(e) {
var n, r, l = [], h = {}, t, c;
for (t = 0, c = e.length; t < c; t++)
n = e[t], h[n.NodeId] = n, h[n.NodeId].children = [];
for (var i in h)
h.hasOwnProperty(i) && (
(r = h[i]).ParentNodeId && "0" !== r.ParentNodeId && "" !== r.ParentNodeId
? (h[r.ParentNodeId].IsParent = !0, h[r.ParentNodeId].children.push(r))
: l.push(r)
);
function s(e, n) {
e.NodeLevel = n;
for (var r = 0; r < e.children.length; r++)
s(e.children[r], n + 1);
}
for (t = 0; t < l.length; t++)
s(l[t], 0);
return l;
## Key Improvements
- **No dependency on input order**: The tree is fully built before `NodeLevel` is calculated.
- **No type coercion issues**: `NodeLevel` is always calculated by numeric addition.
- **Recursive traversal**: Guarantees correct depth assignment for all nodes regardless of tree depth.
Hi Reishi,
Thanks for reporting this bug. When we have time, we'll check it out and fix it.