9
Views
1
Comments
[GTree] Bug Report: `unflatten` function calculates `NodeLevel` incorrectly
gtree
Reactive icon
Forge asset by Pedro Neto
Application Type
Reactive

# 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.


```js

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.

2020-09-15 13-07-23
Kilian Hekhuis
 
MVP

Hi Reishi,

Thanks for reporting this bug. When we have time, we'll check it out and fix it.

Community GuidelinesBe kind and respectful, give credit to the original source of content, and search for duplicates before posting.