Skip to main content

INTEGRATIONS

How to use data-structure-typed with React, Express, Nest.js, and other frameworks.

Back to READMECode ExamplesPerformance


Table of Contents

  1. React Integration
  2. Express Integration
  3. Nest.js Integration
  4. TypeScript Configuration

React Integration

Use Case: Sorted State Management

import { useCallback, useMemo, useState } from 'react';
import { RedBlackTree } from 'data-structure-typed';

interface TodoItem {
id: number;
text: string;
completed: boolean;
priority: number;
}

/**
* ╔═════════════════════════════════════════════════════════════════════════╗
* ║ PERFORMANCE COMPARISON TABLE ║
* ╠═════════════════╦═══════════════════╦═════════════════╦═════════════════╣
* ║ Operation ║ RedBlackTree ║ Array ║ Speedup ║
* ╠═════════════════╬═══════════════════╬═════════════════╬═════════════════╣
* ║ Add todo ║ O(log n) ║ O(n log n) ║ Often much faster* ║
* ║ Delete todo ║ O(log n) ║ O(n) ║ Often faster* ║
* ║ Keep sorted ║ Automatic ✓ ║ Manual sort ✗ ║ Less code ║
* ║ Rebalancing ║ Self-balancing ✓ ║ N/A ║ N/A ║
* ╠═════════════════╩═══════════════════╩═════════════════╩══════════════════╣
* ║ *See PERFORMANCE.md for measured benchmarks and how results scale. ║
* ╚═════════════════════════════════════════════════════════════════════════╝
*/
export default function TodoApp() {
const [text, setText] = useState('');
const [priority, setPriority] = useState(5);

const [todos, setTodos] = useState(() =>
new RedBlackTree<TodoItem>([], {
// Comparator ensures todos are ALWAYS sorted by priority (descending)
// RedBlackTree maintains this order automatically on every insertion
// With Array, you'd need to manually sort() after each add → expensive!
comparator: (a, b) => b.priority - a.priority
}));

const addTodo = useCallback(() => {
if (!text.trim()) return;

setTodos((prev) => {
const next = prev.clone();

// This insertion maintains sorted order automatically
// The Red-Black Tree algorithm handles all the balancing
next.add({
id: Date.now(),
text: text.trim(),
completed: false,
priority: Math.max(1, Math.min(10, priority)),
});
return next;
});

}, [text, priority]);

/**
* Delete a todo efficiently
*
* Performance Analysis:
* ──────────────────
* ✅ RedBlackTree.delete(): O(log n)
* - For 1000 items: ~10 tree operations
* - No need to shift array elements
* - Red-Black Tree rebalances automatically
*
* ❌ Array Alternative:
* - Array.findIndex(): O(n) to find the item
* - Array.splice(): O(n) to remove and shift all elements
* - For 1000 items: ~500 operations per delete
* - Note: Actual wall-clock impact depends on your workload (rendering, GC, object shapes, etc.).
*/
const deleteTodo = useCallback((todo: TodoItem) => {
setTodos((prev) => {
const next = prev.clone();
next.delete(todo); // ← O(log n) deletion
return next;
});
}, []);

const todoList = useMemo(() => [...todos.keys()], [todos]);

return (
<div className="todo-app">
<h2>Priority Todos (Auto-Sorted)</h2>
<div className="input-section">
<input
type="text"
placeholder="Todo text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<input
type="number"
min="1"
max="10"
placeholder="Priority (1-10)"
value={priority}
onChange={(e) =>
setPriority(Math.max(1, Math.min(10, Number(e.target.value))))
}
/>
<button onClick={addTodo} disabled={!text.trim()}>
Add Todo
</button>
</div>
<ul className="todo-list">
{todoList.map((todo) => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => {
setTodos((prev) => {
const next = prev.clone();

// Delete the old todo object
next.delete(todo);
next.add({ ...todo, completed: !todo.completed });
return next;
});
}}
/>
<span>{todo.text}</span>
<span className="priority">P{todo.priority}</span>
<button onClick={() => deleteTodo(todo)}>Delete</button>
</li>
))}
</ul>
</div>
);
}

Express Integration

Use Case: LRU Cache Middleware

const  {DoublyLinkedList} = require('data-structure-typed');

class LRUCache {
constructor(capacity = 100) {
this.cache = new Map();
this.order = new DoublyLinkedList();
this.capacity = capacity;
}
get(key) {
if (!this.cache.has(key))
return null;
const { value, node } = this.cache.get(key);
// Move to end (most recently used)
this.order.delete(node);
const newNode = this.order.push(key);
this.cache.set(key, { value, node: newNode });
return value;
}
set(key, value) {
if (this.cache.has(key)) {
this.get(key); // Mark as recently used
this.cache.get(key).value = value;
return;
}
if (this.cache.size >= this.capacity) {
const lru = this.order.shift();
this.cache.delete(lru);
}
const node = this.order.push(key);
this.cache.set(key, { value, node });
}
}

module.exports = LRUCache;
// Usage in Express
const app = express();
const responseCache = new LRUCache(50);
app.use((req, res, next) => {
const cachedResponse = responseCache.get(req.url);
if (cachedResponse) {
return res.status(cachedResponse.status).json(cachedResponse.data);
}
// Wrap original send to cache response
const originalSend = res.send;
res.send = function (data) {
responseCache.set(req.url, { status: res.statusCode, data });
return originalSend.call(this, data);
};
next();
});
app.get('/api/data', (req, res) => {
res.json({ message: 'Cached response' });
});
app.listen(3000);

Use Case: Rate Limiting with Deque

import { Deque } from 'data-structure-typed';

class RateLimiter {
private requests = new Map<string, Deque<number>>();
private limit: number;
private windowMs: number;

constructor(limit: number = 100, windowMs: number = 60000) {
this.limit = limit;
this.windowMs = windowMs;
}

isAllowed(clientId: string): boolean {
const now = Date.now();

if (!this.requests.has(clientId)) {
this.requests.set(clientId, new Deque());
}

const deque = this.requests.get(clientId)!;

// Remove old requests outside window
while (!deque.isEmpty && deque.peekFirst()! < now - this.windowMs) {
deque.shift();
}

// Check limit
if (deque.size >= this.limit) {
return false;
}

// Add new request
deque.push(now);
return true;
}
}

// Usage in Express
const app = express();
const limiter = new RateLimiter(10, 60000); // 10 requests per minute

app.use((req: Request, res: Response, next: Function) => {
const clientId = req.ip;

if (!limiter.isAllowed(clientId)) {
return res.status(429).json({ error: 'Too many requests' });
}

next();
});

app.get('/api/data', (req, res) => {
res.json({ data: 'Your data here' });
});

Nest.js Integration

Use Case: Product Price Index Service

Full working demo: StackBlitz NestJS Playground

import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';

import { Range, RedBlackTree } from 'data-structure-typed';

export interface Product {
id: string;
name: string;
price: number;
quantity: number;
category: string;
lastUpdated?: Date;
}

interface CompositeKey {
price: number;
productId: string;
}

export type TierName = 'budget' | 'mid-range' | 'premium';

/**
* Product Price Index Service using Red-Black Tree with Composite Keys
*
* ⭐ Performance vs Alternatives:
*
* Operation | RBTree(this approach) | Array | HashMap + Sort
* -------------------|-----------------------|-----------|---------------
* Range Query | O(log n + k) | O(n) | O(k log k)
* Point Lookup | O(1) | O(1) | O(1)
* Insert/Update | O(log n) | O(n) | O(log n)
* Sort by Price | O(n) | O(n log n)| O(n log n)
* Pagination | O(log n + k) | O(n log n)| O(n log n)
* Rank/Percentile | O(log n) | O(n) | O(n log n)
*/
@Injectable()
export class ProductPriceIndexService {
private priceIndex: RedBlackTree<CompositeKey, Product>;
private idToKeyMap: Map<string, CompositeKey>;

constructor() {
this.priceIndex = new RedBlackTree([], {
comparator: (a: CompositeKey, b: CompositeKey) => {
const priceCmp = a.price - b.price;
if (priceCmp !== 0) return priceCmp;
return a.productId.localeCompare(b.productId);
},
enableOrderStatistic: true, // Enables getRank/getByRank/rangeByRank
});
this.idToKeyMap = new Map();
}

/** O(log n) */
addProduct(product: Product): Product {
if (this.idToKeyMap.has(product.id))
throw new BadRequestException(`Product ${product.id} already exists`);

product.lastUpdated = new Date();
const key: CompositeKey = { price: product.price, productId: product.id };
this.priceIndex.set(key, product);
this.idToKeyMap.set(product.id, key);
return product;
}

/** O(1) — HashMap lookup */
getProductById(productId: string): Product {
const key = this.idToKeyMap.get(productId);
if (key === undefined)
throw new NotFoundException(`Product ${productId} not found`);
return this.priceIndex.get(key)!;
}

/** O(log n + k) — directly returns values, no secondary lookup */
getProductsByPriceRange(minPrice: number, maxPrice: number): Product[] {
const range = new Range(
{ price: minPrice, productId: '' },
{ price: maxPrice, productId: '\uffff' },
true,
true,
);
return this.priceIndex.rangeSearch(range, (n) => n.value!);
}

/** O(log n) — NavigableMap floor() */
getHighestPricedProductWithinBudget(maxBudget: number): Product | null {
const key: CompositeKey = { price: maxBudget, productId: '\uffff' };
const floorKey = this.priceIndex.floor(key);
return floorKey ? this.priceIndex.get(floorKey)! : null;
}

/** O(n) — iterator protocol, one-liner */
getAllProductsSortedByPrice(): Product[] {
return [...this.priceIndex.values()];
}

/**
* O(n) for totalValue, O(log n) for min/max
* min/max use getLeftMost/getRightMost instead of full traversal
*/
getStatistics() {
if (this.idToKeyMap.size === 0)
return { totalProducts: 0, priceRange: { min: 0, max: 0 }, averagePrice: 0, totalValue: 0 };

const minKey = this.priceIndex.getLeftMost();
const maxKey = this.priceIndex.getRightMost();

let totalValue = 0;
let totalProducts = 0;
for (const [, product] of this.priceIndex) {
totalValue += product.price * product.quantity;
totalProducts += product.quantity;
}

return {
totalProducts,
priceRange: { min: minKey?.price ?? 0, max: maxKey?.price ?? 0 },
averagePrice: totalValue / totalProducts,
totalValue,
};
}

// ── Order-Statistic API (rank-based queries) ──────────────────────

/** O(log n + pageSize) — rank-based pagination */
getProductsByPage(page: number, pageSize: number): Product[] {
const start = page * pageSize;
const end = Math.min(start + pageSize - 1, this.priceIndex.size - 1);
if (start >= this.priceIndex.size) return [];
const keys = this.priceIndex.rangeByRank(start, end);
return keys.map((key) => this.priceIndex.get(key)!);
}

/** O(log n) — what % of products are cheaper */
getPricePercentile(productId: string): number {
const key = this.idToKeyMap.get(productId);
if (!key) throw new NotFoundException(`Product ${productId} not found`);
return (this.priceIndex.getRank(key) / this.priceIndex.size) * 100;
}

/** O(log n) — median product */
getMedianProduct(): Product | null {
if (this.priceIndex.size === 0) return null;
const key = this.priceIndex.getByRank(Math.floor((this.priceIndex.size - 1) / 2));
return key ? this.priceIndex.get(key)! : null;
}

/** O(log n + k) — top N cheapest */
getTopNCheapest(n: number): Product[] {
const end = Math.min(n - 1, this.priceIndex.size - 1);
if (end < 0) return [];
return this.priceIndex.rangeByRank(0, end).map((key) => this.priceIndex.get(key)!);
}

/** O(log n + k) — top N most expensive */
getTopNExpensive(n: number): Product[] {
const size = this.priceIndex.size;
if (size === 0) return [];
const start = Math.max(size - n, 0);
return this.priceIndex.rangeByRank(start, size - 1)
.map((key) => this.priceIndex.get(key)!)
.reverse();
}

/** O(log n) — dynamic tier by percentile, no hardcoded price ranges */
getTierByPercentile(productId: string): TierName {
const pct = this.getPricePercentile(productId);
if (pct < 33) return 'budget';
if (pct < 66) return 'mid-range';
return 'premium';
}
}

Use Case: Task Queue Controller

import { Controller, Post, Body, Get } from '@nestjs/common';
import { MaxPriorityQueue } from 'data-structure-typed';

export interface QueuedTask {
id: string;
action: string;
priority: number;
createdAt: Date;
}

@Controller('tasks')
export class TaskController {
private taskQueue = new MaxPriorityQueue<QueuedTask>([], {
comparator: (a, b) => a.priority - b.priority,
});

@Post('add')
addTask(@Body() task: QueuedTask): { success: boolean; queueSize: number } {
this.taskQueue.add(task);
return {
success: true,
queueSize: this.taskQueue.size,
};
}

@Post('process')
processNext(): { success: boolean; task: QueuedTask | null } {
const task = this.taskQueue.poll();
return {
success: !!task,
task: task || null,
};
}

@Get('queue-size')
getQueueSize(): { size: number } {
return { size: this.taskQueue.size };
}
}

TypeScript Configuration

tsconfig.json for data-structure-typed

{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"lib": ["ES2020", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}

Package.json Setup

{
"dependencies": {
"data-structure-typed": "^latest"
},
"devDependencies": {
"typescript": "^5.0.0",
"@types/node": "^latest"
}
}

Import Patterns

All Structures

import {
// Trees
BST,
AVLTree,
RedBlackTree,
BSTNode,
TreeMultiMap,

// Linear
Stack,
Queue,
Deque,
LinkedList,
SinglyLinkedList,
DoublyLinkedList,

// Heap
Heap,
MinHeap,
MaxHeap,
MinPriorityQueue,
MaxPriorityQueue,

// Special
Trie,
DirectedGraph,
UndirectedGraph,

// Utilities
SegmentTree,
FenwickTree
} from 'data-structure-typed';

Module Systems

// ES Module (ESM)
import { RedBlackTree } from 'data-structure-typed';

// CommonJS
const { RedBlackTree } = require('data-structure-typed');

// TypeScript with full typing
import { RedBlackTree } from 'data-structure-typed/dist/esm/red-black-tree';

// CDN (for browsers)
<script src="https://cdn.jsdelivr.net/npm/data-structure-typed/dist/umd/data-structure-typed.min.js"></script>
<script>
const tree = new DataStructureTyped.RedBlackTree();
</script>

Common Integration Patterns

Pattern 1: Dependency Injection (Nest.js)

import { Module } from '@nestjs/common';
import { ProductInventoryService } from './product-inventory.service';

@Module({
providers: [ProductInventoryService],
exports: [ProductInventoryService]
})
export class ProductInventoryModule {}

// Use in other services
@Injectable()
export class OutherService {
constructor(private inventoryService: ProductInventoryService) {}

async addProduct(product: Product): Promise<void> {
this.inventoryService.addProduct(product);
}
}

Pattern 2: React Hooks

import {useCallback, useRef, useState} from 'react';
import {RedBlackTree} from 'data-structure-typed';

function useSortedList<T>(initialData: T[] = []) {
const treeRef = useRef(new RedBlackTree<number, T>(initialData));
const [, setUpdateTrigger] = useState({});

const add = useCallback((index: number, item: T) => {
treeRef.current.set(index, item);
setUpdateTrigger({});
}, []);

const remove = useCallback((index: number) => {
treeRef.current.delete(index);
setUpdateTrigger({});
}, []);

const getAll = useCallback(() => {
return [...treeRef.current.values()];
}, []);

return {add, remove, getAll};
}

// Usage
function MyComponent() {
const {add, remove, getAll} = useSortedList<string>([]);

return (
<div>
<button onClick={() => add(1, 'Item 1')}>Add Item</button>
<ul>
{getAll().map((item, i) => (
<li key={i} onClick={() => remove(i)}>{item}</li>
))}
</ul>
</div>
);
}

export default MyComponent;

Troubleshooting

Issue: Import Error

// ❌ Wrong
import RedBlackTree from 'data-structure-typed';

// ✅ Correct
import { RedBlackTree } from 'data-structure-typed';

Issue: Type Not Found

// Make sure TypeScript configuration is correct
// and you have the latest type definitions
npm update data-structure-typed

Issue: Performance in Development

// Development mode is slower due to hot reload
// Use production build for benchmarking:
npm run build
NODE_ENV=production node your-app.js

Need more examples? Check GUIDES.md for more patterns.

Performance questions? See PERFORMANCE.md.