@httpx/treeu v0.4.6
@httpx/treeu
Fast and lightweight (~300B) utilities to work with trees.
Install
$ npm install @httpx/treeu
$ yarn add @httpx/treeu
$ pnpm add @httpx/treeu
Features
- šĀ Lightweight (starts at ~350B) and node, browser and edge support.
- šĀ Available in ESM and CJS formats.
Documentation
š Official website or Github Readme
Usage
Search
DFSTreeSearch
Depth-First Search (DFS) algorithm for tree structures. It uses a stack rather than recursion in order to support deeply nested trees without call-stack overflows. It is well suited for exploring a branch of a data structure in depth and usually preferred when memory usage is a concern or when the data structure has many nodes with few levels.
import { Tree, type TreeNode } from '@httpx/treeu';
type CustomValue =
| { type: 'folder'; size?: never }
| { type: 'file'; size: number };
const treeNodes: TreeNode<CustomValue>[] = [
{
id: 'file2.ts',
parentId: null,
value: { size: 10, type: 'file' },
children: [],
},
{
id: 'folder1',
parentId: null,
value: { type: 'folder' },
children: [
{
id: 'folder1/file1.ts',
parentId: 'folder1',
value: { size: 30, type: 'file' },
children: [],
},
],
},
];
const search = new DfsTreeSearch<CustomValue>(treeNodes);
const res1 = search.findOne('folder1/file1.ts');
const res2 = search.findOne(['id', '===', 'folder1/file1.ts']);
const res3 = search.findOne((treeNode) => treeNode.value.size === 30);
const res4 = search.findOne(['parentId', '===', 'folder1']);
// res1 === res2 === res3 === res4
Mapper
FlatTreeWsMapper
FlatTreeWsMapper | Description |
---|---|
toTreeNodes | Convert to flat map with separator |
fromTreeNodes | Convert a tree to a flat map with separator |
import { FlatTreeWsMapper, type FlatTreeWs } from '@httpx/treeu';
type CustomValue =
| { type: 'folder'; size?: never }
| { type: 'file'; size: number };
// Use an object or a Record<string, CustomValue>
const paths: FlatTreeWs<CustomValue> = new Map([
[ 'file1.ts', { type: 'file', size: 10 } ],
[ 'folder1', { type: 'folder' }],
[ 'folder1/file1.ts', { type: 'file', size: 30 }],
]);
const mapper = new FlatTreeWsMapper<CustomValue>();
const treeResult = mapper.toTreeNodes(paths, {
separator: '/',
});
// Will return
const expected: TreeNode<CustomValue>[] = [
{
id: 'file1.ts',
parentId: null,
value: {
size: 10,
type: 'file',
},
children: [],
},
{
id: 'folder1',
parentId: null,
value: {
type: 'folder',
},
children: [
{
id: 'folder1/file1.ts',
parentId: 'folder1',
value: {
size: 30,
type: 'file',
},
children: [],
},
],
},
];
TreeNode
Example
Example of TreeNode[]
typing:
import { Tree, TreeNode } from '@httpx/treeu';
type CustomValue =
| { type: 'folder'; size?: never }
| { type: 'file'; size: number };
const treeNodes: TreeNode<CustomValue>[] = [
{
id: 'file1.ts',
parentId: null,
value: {
size: 10,
type: 'file',
},
children: [],
},
{
id: 'file2.ts',
parentId: null,
value: {
size: 20,
type: 'file',
},
children: [],
},
{
id: 'folder1',
parentId: null,
value: {
type: 'folder',
},
children: [
{
id: 'folder1/file1.ts',
parentId: 'folder1',
value: {
size: 30,
type: 'file',
},
children: [],
},
],
},
{
id: 'folder2',
parentId: null,
value: {
type: 'folder',
},
children: [
{
id: 'folder2/file1.ts',
parentId: 'folder2',
value: {
size: 40,
type: 'file',
},
children: [],
},
{
id: 'folder2/subfolder1',
parentId: 'folder2',
value: {
type: 'folder',
},
children: [
{
id: 'folder2/subfolder1/file1.ts',
parentId: 'folder2/subfolder1',
value: {
size: 50,
type: 'file',
},
children: [],
},
],
},
],
},
{
id: 'folder3',
parentId: null,
value: {
type: 'folder',
},
children: [],
},
];
Tree
Types
TreeNode<TValue, TId> | Type | Description |
---|---|---|
id | TValue extends string\|number | Unique identifier of the node. |
parentId | TValue extends string\|number | Reference to the parent node |
children | TreeNode<TValue, TId>[] | Children nodes |
value | TValue\|undefined | Custom value associated with the node |
Benchmarks
Performance is continuously monitored thanks to codspeed.io.
RUN v2.0.5
ā bench/search.bench.ts (4) 6714ms
ā Bench search (10_000 entries) (4) 6713ms
name hz min max mean p75 p99 p995 p999 rme samples
· DfsTreeSearch.findOne(id_0) over 10_000 9,360,159.92 0.0001 3.5234 0.0001 0.0001 0.0002 0.0002 0.0004 ±1.88% 4680081 fastest
· DfsTreeSearch.findOne(id_1000) over 10_000 64,337.37 0.0097 0.7432 0.0155 0.0151 0.0437 0.0752 0.2380 ±1.09% 32169
· DfsTreeSearch.findOne(id_5000) over 10_000 13,027.78 0.0505 0.9968 0.0768 0.0753 0.2389 0.2790 0.4839 ±1.33% 6514
· DfsTreeSearch.findOne(id_NotExists) over 10_000 6,335.21 0.1020 0.8740 0.1578 0.1582 0.4216 0.4970 0.6655 ±1.47% 3168 slowest
ā bench/mapper.bench.ts (1) 621ms
ā Bench mapper (10_000 entries) (1) 620ms
name hz min max mean p75 p99 p995 p999 rme samples
· FlatTreeWsMapper.toTreeNodesOrThrow 323.01 2.1707 9.7894 3.0959 3.5083 9.2491 9.7894 9.7894 ±5.95% 162
BENCH Summary
FlatTreeWsMapper.toTreeNodesOrThrow - bench/mapper.bench.ts > Bench mapper (10_000 entries)
DfsTreeSearch.findOne(id_0) over 10_000 - bench/search.bench.ts > Bench search (10_000 entries)
145.49x faster than DfsTreeSearch.findOne(id_1000) over 10_000
718.48x faster than DfsTreeSearch.findOne(id_5000) over 10_000
1477.48x faster than DfsTreeSearch.findOne(id_NotExists) over 10_000
See benchmark file for details.
Bundle size
Bundle size is tracked by a size-limit configuration
Scenario (esm) | Size (compressed) |
---|---|
import { DfsTreeSearch } from '@httpx/treeu | ~ 350B |
import { FlatTreeWsMapper } from '@httpx/treeu | ~ 800B |
For CJS usage (not recommended) track the size on bundlephobia.
Compatibility
Level | CI | Description |
---|---|---|
Node | ā | CI for 18.x, 20.x & 22.x. |
Browser | ā | Tested with latest chrome (vitest/playwright) |
Browsers | ā | > 96% on 07/2024. Mins to Chrome 96+, Firefox 90+, Edge 19+, iOS 12+, Safari 12+, Opera 77+ |
Edge | ā | Ensured on CI with @vercel/edge-runtime. |
Cloudflare | ā | Ensured with @cloudflare/vitest-pool-workers (see wrangler.toml |
Edge | ā | Ensured on CI with @vercel/edge-runtime. |
Typescript | ā | TS 5.0 + / are-the-type-wrong checks on CI. |
ES2022 | ā | Dist files checked with es-check |
Performance | ā | Monitored with codspeed.io |
For older browsers: most frontend frameworks can transpile the library (ie: nextjs...)
Contributors
Contributions are welcome. Have a look to the CONTRIBUTING document.
Sponsors
If my OSS work brightens your day, let's take it to new heights together! Sponsor, coffee, or star ā any gesture of support fuels my passion to improve. Thanks for being awesome! šā¤ļø
Special thanks to
License
MIT Ā© belgattitude and contributors.