map-tree-utils
Version:
Tiny TypeScript utilities to convert between flat Map<string, T> structures and nested tree arrays
207 lines (146 loc) • 6.17 kB
Markdown


> Tiny TypeScript utilities to convert between flat Map<string, T> structures and nested tree arrays.
```bash
npm install map-tree-utils
yarn add map-tree-utils
pnpm add map-tree-utils
bun i map-tree-utils
```
When working with hierarchical data, you often face two common tasks:
1. You receive a nested tree (e.g., from an API or authored JSON) and want to normalize it into a flat Map for fast lookups and updates.
2. You have a flat structure (e.g., a `Map` keyed by `id`) and need to render it as a nested tree for UI or export.
`map-tree-utils` provides two small, focused helpers with zero runtime dependencies to handle this:
- `getTree` — convert a `Map<string, T>` (flat) into a nested array of root nodes (tree).
- `getMap` — convert a nested array (tree) into a `Map<string, AnyObj>` (flat).
Written in TypeScript, both functions are simple, predictable, and easy to drop into any frontend or backend project. They save you from writing repetitive boilerplate code while keeping your data handling fast and type-safe.
## Quick example
```ts
import { getTree, getMap } from "map-tree-utils";
// --- Example: flat Map representing hierarchical data ---
const map = new Map([
["1", { id: "1", name: "Root" }],
["2", { id: "2", name: "Child A", parentId: "1" }],
["3", { id: "3", name: "Child B", parentId: "1" }],
["4", { id: "4", name: "Grandchild", parentId: "2" }],
]);
// Convert flat Map -> nested tree
const tree = getTree(map);
console.log("Nested tree:");
console.log(JSON.stringify(tree, null, 2));
// Convert back: nested tree -> flat Map
const flatMap = getMap(tree);
console.log("Flattened Map, item with id '4':");
console.log(flatMap.get("4"));
// Output: { id: "4", name: "Grandchild", parentId: "2" }
// Get sorted tree: flat Map -> sorted tree
const stortedTree = getTree(map, "children", "parentId", "ord", "asc");
console.log("Sorted tree:");
console.log(JSON.stringify(stortedTree, null, 2));
```
Minimal example
demo:
[ ](https://evgen002.github.io/map-tree-demo/)
```tsx
import { useMemo, useState } from "react";
import { getMap, getTree } from "map-tree-utils";
interface Tree {
code: string;
name: string;
childs?: Tree[];
}
const tree: Tree[] = [
{
code: "1",
name: "one",
childs: [
{ code: "1_1", name: "child_one" },
{ code: "1_2", name: "child_two" },
],
},
];
function App() {
const [normalized, setNormalized] = useState(
getMap(tree, "childs", "parentCode", "code")
);
const update = () => {
setNormalized((prev) => {
const map = new Map(prev);
const prevTarget = map.get("1_2");
if (prevTarget) {
map.set("1_2", { ...prevTarget, name: "John" });
return map;
} else {
return prev;
}
});
};
const updatedTree = useMemo(() => {
return getTree(normalized, "childs", "parentCode");
}, [normalized]);
return (
<div className="example">
<h1>Map Tree Utils - DEMO</h1>
<pre>{JSON.stringify(normalized.get("1_2"))}</pre>
<TreeNode tree={updatedTree} />
<button onClick={update}>update</button>
</div>
);
}
function TreeNode({ tree }: { tree: Tree[] }) {
return (
<ul>
{tree.map((node) => (
<li key={node.code}>
{node.name}
{node.childs && node.childs.length > 0 && (
<TreeNode tree={node.childs} />
)}
</li>
))}
</ul>
);
}
export default App;
```
```ts
getTree<T>(map: Map<string, T>, childKey = "children", parentKey = "parentId"): Array<T & AnyObj>
```
Converts a `Map` of nodes into a nested array of root nodes.
**Parameters**
- `map: Map<string, T>` - a Map where each value is a node object containing an identifier and optionally a parent reference.
- `childKey: string` - the key to use for children arrays in the output nodes. Default: "children".
- `parentKey: string` - the key used on nodes to identify their parent. Default: "parentId".
- `sortBy: string | (a, b) => number` - used to return sorted tree
**Returns**
An array of nodes that are roots (nodes with no parent found in the provided Map). Each node in the returned tree is a shallow copy of the original node with an added children (or custom childKey) array.
**Notes**
- The function creates shallow copies of nodes so the original Map values are not mutated.
- If a node references a `parentKey: string` that does not exist in the Map, the node will be treated as a root.
#
```ts
getMap(tree: AnyObj[], childKey = "children", parentKey = "parentId", idKey = "id"): Map<string, AnyObj>
```
Flattens a nested tree array into a `Map<string, AnyObj>` keyed by id (or a custom idKey).
**Parameters**
- `tree: AnyObj[]` - array of tree nodes (roots).
- `childKey: string` - property name that holds child nodes. Default: "children".
- `parentKey: string` - property name to be assigned to flattened nodes to indicate their parent id. Default: "parentId".
- `idKey: string` - property name used in the original nodes to identify them. Default: "id".
**Returns**
A `Map<string, AnyObj>` where each key is the stringified id and each value is a shallow copy of the node (the children property is removed). Each child node will have a parentId pointing to its parent in the flattened structure.
**Notes**
The function will coerce the id value to String() when using it as a Map key.
children arrays are omitted in the flattened node objects.
## Performance
Both utilities walk every node exactly once and perform O(N) operations (plus Map lookups which are O(1) amortized). They are suitable for reasonably sized trees used in frontend apps or typical backend data normalization tasks.
**License: MIT**