Complete Documentation โ
Key Features โ
- ๐ฏ SQL-like Wildcards - Use
%and_for flexible pattern matching - ๐ Deep Object Filtering - Search through nested objects up to configurable depths
- โก Zero Dependencies - Lightweight and production-ready (only Zod for runtime validation)
- ๐ Type-Safe - Built with strict TypeScript for maximum reliability
- ๐จ Multiple Strategies - String patterns, objects, predicates, operators, or custom comparators
- ๐ Performance Optimized - Optional caching and regex compilation optimization
- ๐งช Battle-Tested - 463+ tests ensuring reliability
- ๐ฆ MongoDB-Style Operators - 18 operators for advanced filtering (v5.0.0+)
- ๐ค Intelligent Autocomplete - Type-aware operator suggestions (v5.2.4+)
- ๐๏ธ Nested Object Support - 4-level deep autocomplete (v5.2.4+)
- ๐จ Lazy Evaluation - Process large datasets efficiently (v5.1.0+)
- ๐ Logical Operators - Complex queries with $and, $or, $not (v5.2.0+)
Why Choose This Library? โ
- Flexible: Multiple ways to filter - choose what fits your use case
- Powerful: Handles complex scenarios that native filter can't
- Type-Safe: Full TypeScript support with strict typing
- Tested: Comprehensive test suite with 100% operator coverage
- Modern: Built with latest best practices and tools
- Documented: Extensive documentation with real-world examples
Installation & Setup โ
Installation โ
Choose your preferred package manager:
# Using npm
npm install @mcabreradev/filter
# Using yarn
yarn add @mcabreradev/filter
# Using pnpm
pnpm add @mcabreradev/filterTypeScript Configuration โ
The library is built with TypeScript and requires Node.js >= 20. For optimal TypeScript experience, ensure your tsconfig.json includes:
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"lib": ["ES2022"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}Import Examples โ
ES Modules (Recommended) โ
import { filter } from '@mcabreradev/filter';
// With types
import type { Expression, FilterOptions } from '@mcabreradev/filter';CommonJS โ
const { filter } = require('@mcabreradev/filter');
// In TypeScript
import filter = require('@mcabreradev/filter');Default Import โ
import filter from '@mcabreradev/filter';Quick Start โ
Get started with the most common use cases:
import { filter } from '@mcabreradev/filter';
const users = [
{ name: 'Alice', age: 30, city: 'Berlin', role: 'admin' },
{ name: 'Bob', age: 25, city: 'London', role: 'user' },
{ name: 'Charlie', age: 35, city: 'Berlin', role: 'user' }
];
// Simple string matching (searches all properties)
filter(users, 'Berlin');
// โ [Alice, Charlie]
// Object-based filtering (exact match)
filter(users, { city: 'Berlin', age: 30 });
// โ [Alice]
// Predicate function
filter(users, (user) => user.age > 28);
// โ [Alice, Charlie]
// MongoDB-style operators (v5.0.0+)
filter(users, { age: { $gte: 25, $lt: 35 } });
// โ [Bob, Alice]Basic Usage โ
String Filtering โ
Filter arrays by searching for a string across all object properties.
Simple String Match โ
const products = [
{ id: 1, name: 'Laptop', brand: 'Dell' },
{ id: 2, name: 'Mouse', brand: 'Logitech' },
{ id: 3, name: 'Keyboard', brand: 'Dell' }
];
// Find all products with "Dell" (case-insensitive by default)
filter(products, 'Dell');
// โ [{ id: 1, name: 'Laptop', brand: 'Dell' }, { id: 3, name: 'Keyboard', brand: 'Dell' }]
filter(products, 'laptop');
// โ [{ id: 1, name: 'Laptop', brand: 'Dell' }]Case-Sensitive Matching โ
// Case-sensitive search
filter(products, 'Dell', { caseSensitive: true });
// โ Finds only exact "Dell" matches
filter(products, 'dell', { caseSensitive: true });
// โ Returns [] (no match)Searching Arrays of Primitives โ
const tags = ['JavaScript', 'TypeScript', 'React', 'Node.js'];
filter(tags, 'script');
// โ ['JavaScript', 'TypeScript']
filter(tags, 'React');
// โ ['React']Number Filtering โ
Filter by numeric values.
const prices = [10, 25, 50, 100, 200];
filter(prices, 50);
// โ [50]
// With objects
const items = [
{ name: 'Item A', price: 10 },
{ name: 'Item B', price: 25 },
{ name: 'Item C', price: 10 }
];
filter(items, 10);
// โ [{ name: 'Item A', price: 10 }, { name: 'Item C', price: 10 }]Boolean Filtering โ
Filter by boolean values.
const tasks = [
{ title: 'Task 1', completed: true },
{ title: 'Task 2', completed: false },
{ title: 'Task 3', completed: true }
];
filter(tasks, true);
// โ [{ title: 'Task 1', completed: true }, { title: 'Task 3', completed: true }]
filter(tasks, false);
// โ [{ title: 'Task 2', completed: false }]Wildcard Patterns โ
SQL-like wildcard patterns for flexible string matching.
Percent Wildcard (%) โ
The % wildcard matches zero or more characters.
Match at Beginning โ
const users = [
{ name: 'Alice' },
{ name: 'Alex' },
{ name: 'Bob' }
];
filter(users, 'Al%');
// โ [{ name: 'Alice' }, { name: 'Alex' }]Match at End โ
filter(users, '%ce');
// โ [{ name: 'Alice' }]Match in Middle โ
const emails = [
{ email: 'user@gmail.com' },
{ email: 'admin@yahoo.com' },
{ email: 'test@gmail.com' }
];
filter(emails, '%gmail%');
// โ [{ email: 'user@gmail.com' }, { email: 'test@gmail.com' }]Multiple Wildcards โ
const files = [
{ name: 'document.pdf' },
{ name: 'image.png' },
{ name: 'archive.tar.gz' }
];
filter(files, '%.p%');
// โ [{ name: 'document.pdf' }, { name: 'image.png' }]Underscore Wildcard (_) โ
The _ wildcard matches exactly one character.
Single Character Match โ
const codes = [
{ code: 'A1' },
{ code: 'A2' },
{ code: 'B1' },
{ code: 'AB1' }
];
filter(codes, 'A_');
// โ [{ code: 'A1' }, { code: 'A2' }]Position-Specific Matching โ
const ids = [
{ id: 'user-101' },
{ id: 'user-102' },
{ id: 'admin-101' }
];
filter(ids, 'user-10_');
// โ [{ id: 'user-101' }, { id: 'user-102' }]Combining _ and % โ
const patterns = [
{ text: 'test' },
{ text: 'testing' },
{ text: 'tested' }
];
filter(patterns, 'test__%');
// โ [{ text: 'testing' }, { text: 'tested' }]Negation (!) โ
Use ! prefix to exclude matches.
Negating Strings โ
const users = [
{ name: 'Alice', city: 'Berlin' },
{ name: 'Bob', city: 'London' },
{ name: 'Charlie', city: 'Berlin' }
];
filter(users, '!London');
// โ [{ name: 'Alice', city: 'Berlin' }, { name: 'Charlie', city: 'Berlin' }]Negating Patterns โ
const emails = [
{ email: 'user@gmail.com' },
{ email: 'admin@company.com' },
{ email: 'test@gmail.com' }
];
filter(emails, '!%gmail%');
// โ [{ email: 'admin@company.com' }]Negation with Wildcards โ
const files = [
{ name: 'doc.pdf' },
{ name: 'image.jpg' },
{ name: 'video.mp4' }
];
filter(files, '!%.pdf');
// โ [{ name: 'image.jpg' }, { name: 'video.mp4' }]Object-Based Filtering โ
Filter by matching object properties.
Single Property Match โ
const products = [
{ id: 1, name: 'Laptop', category: 'Electronics', price: 1200 },
{ id: 2, name: 'Desk', category: 'Furniture', price: 350 },
{ id: 3, name: 'Mouse', category: 'Electronics', price: 25 }
];
// Match by single property
filter(products, { category: 'Electronics' });
// โ [{ id: 1, ... }, { id: 3, ... }]
filter(products, { price: 350 });
// โ [{ id: 2, name: 'Desk', ... }]Multiple Properties (AND Logic) โ
All specified properties must match.
filter(products, { category: 'Electronics', price: 1200 });
// โ [{ id: 1, name: 'Laptop', ... }]
filter(products, { category: 'Electronics', price: 25 });
// โ [{ id: 3, name: 'Mouse', ... }]Nested Objects โ
Filter by nested object properties.
const users = [
{
name: 'Alice',
address: { city: 'Berlin', country: 'Germany' },
settings: { theme: 'dark', language: 'en' }
},
{
name: 'Bob',
address: { city: 'London', country: 'UK' },
settings: { theme: 'light', language: 'en' }
}
];
// Direct nested property match
filter(users, { address: { city: 'Berlin' } });
// โ [{ name: 'Alice', ... }]
// Multiple nested properties
filter(users, {
address: { country: 'Germany' },
settings: { theme: 'dark' }
});
// โ [{ name: 'Alice', ... }]Mixing with Wildcards โ
const items = [
{ name: 'Product A', code: 'PROD-001' },
{ name: 'Product B', code: 'PROD-002' },
{ name: 'Service A', code: 'SERV-001' }
];
filter(items, { code: 'PROD-%' });
// โ [{ name: 'Product A', ... }, { name: 'Product B', ... }]Predicate Functions โ
Use custom functions for complex filtering logic.
Basic Predicates โ
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Simple condition
filter(numbers, (n) => n > 5);
// โ [6, 7, 8, 9, 10]
// Even numbers
filter(numbers, (n) => n % 2 === 0);
// โ [2, 4, 6, 8, 10]
// Odd numbers
filter(numbers, (n) => n % 2 !== 0);
// โ [1, 3, 5, 7, 9]Advanced Predicates โ
const products = [
{ name: 'Laptop', price: 1200, inStock: true, rating: 4.5 },
{ name: 'Mouse', price: 25, inStock: true, rating: 4.0 },
{ name: 'Keyboard', price: 75, inStock: false, rating: 4.2 }
];
// Complex conditions
filter(products, (product) =>
product.price < 100 &&
product.inStock &&
product.rating >= 4.0
);
// โ [{ name: 'Mouse', ... }]
// String operations
filter(products, (product) =>
product.name.toLowerCase().includes('key')
);
// โ [{ name: 'Keyboard', ... }]
// Date comparisons
const orders = [
{ id: 1, date: new Date('2025-01-15'), amount: 100 },
{ id: 2, date: new Date('2025-02-20'), amount: 200 },
{ id: 3, date: new Date('2025-03-10'), amount: 150 }
];
filter(orders, (order) =>
order.date >= new Date('2025-02-01') &&
order.amount > 150
);
// โ [{ id: 2, ... }]Type-Safe Predicates with TypeScript โ
interface User {
id: number;
name: string;
age: number;
isActive: boolean;
}
const users: User[] = [
{ id: 1, name: 'Alice', age: 30, isActive: true },
{ id: 2, name: 'Bob', age: 25, isActive: false },
{ id: 3, name: 'Charlie', age: 35, isActive: true }
];
// Fully typed predicate
filter<User>(users, (user: User): boolean =>
user.age > 28 && user.isActive
);
// โ [{ id: 1, ... }, { id: 3, ... }]Performance Considerations โ
// โ
Good: Simple, direct checks
filter(items, (item) => item.price > 100);
// โ ๏ธ Slower: Complex operations
filter(items, (item) => {
const calculated = item.price * 1.2 + item.tax;
return calculated > 100 && item.category.toLowerCase().includes('electronics');
});
// ๐ก Better: Use operators when possible
filter(items, {
price: { $gt: 83.33 }, // Pre-calculated
category: { $contains: 'electronics' }
});MongoDB-Style Operators โ
Version 5.0.0 introduces 13 powerful MongoDB-style operators for advanced filtering.
Comparison Operators โ
$gt - Greater Than โ
Returns items where the property value is greater than the specified value.
const products = [
{ name: 'Item A', price: 50 },
{ name: 'Item B', price: 150 },
{ name: 'Item C', price: 250 }
];
filter(products, { price: { $gt: 100 } });
// โ [{ name: 'Item B', price: 150 }, { name: 'Item C', price: 250 }]
// With dates
const orders = [
{ id: 1, date: new Date('2025-01-15') },
{ id: 2, date: new Date('2025-02-20') },
{ id: 3, date: new Date('2025-03-10') }
];
filter(orders, { date: { $gt: new Date('2025-02-01') } });
// โ [{ id: 2, ... }, { id: 3, ... }]$gte - Greater Than or Equal โ
filter(products, { price: { $gte: 150 } });
// โ [{ name: 'Item B', price: 150 }, { name: 'Item C', price: 250 }]
// Date range start
filter(orders, {
date: { $gte: new Date('2025-02-01') }
});
// โ Includes orders from Feb 1st onwards$lt - Less Than โ
filter(products, { price: { $lt: 200 } });
// โ [{ name: 'Item A', price: 50 }, { name: 'Item B', price: 150 }]
// Before a date
filter(orders, {
date: { $lt: new Date('2025-03-01') }
});
// โ Orders before March 1st$lte - Less Than or Equal โ
filter(products, { price: { $lte: 150 } });
// โ [{ name: 'Item A', price: 50 }, { name: 'Item B', price: 150 }]$eq - Equal โ
const users = [
{ name: 'Alice', role: 'admin', age: 30 },
{ name: 'Bob', role: 'user', age: 25 },
{ name: 'Charlie', role: 'admin', age: 35 }
];
filter(users, { role: { $eq: 'admin' } });
// โ [{ name: 'Alice', ... }, { name: 'Charlie', ... }]
// Equivalent to simple object matching
filter(users, { role: 'admin' });
// โ Same result$ne - Not Equal โ
filter(users, { role: { $ne: 'admin' } });
// โ [{ name: 'Bob', role: 'user', ... }]
filter(products, { price: { $ne: 50 } });
// โ All products except Item ARange Queries (Combining Operators) โ
// Price between $100 and $200 (inclusive)
filter(products, {
price: { $gte: 100, $lte: 200 }
});
// โ [{ name: 'Item B', price: 150 }]
// Date range
filter(orders, {
date: {
$gte: new Date('2025-01-01'),
$lte: new Date('2025-02-29')
}
});
// โ Orders in Q1 2025
// Excluding boundaries
filter(products, {
price: { $gt: 50, $lt: 250 }
});
// โ [{ name: 'Item B', price: 150 }]Array Operators โ
$in - Inclusion โ
Returns items where the property value is in the specified array.
const products = [
{ id: 1, category: 'Electronics' },
{ id: 2, category: 'Furniture' },
{ id: 3, category: 'Books' },
{ id: 4, category: 'Clothing' }
];
filter(products, {
category: { $in: ['Electronics', 'Books'] }
});
// โ [{ id: 1, ... }, { id: 3, ... }]
// With numbers
const items = [
{ id: 1, status: 200 },
{ id: 2, status: 404 },
{ id: 3, status: 500 }
];
filter(items, {
status: { $in: [200, 201, 202] }
});
// โ [{ id: 1, status: 200 }]
// Mixed types
filter(products, {
id: { $in: [1, 3, 5, 7] }
});
// โ Products with IDs 1 and 3$nin - Not In (Exclusion) โ
filter(products, {
category: { $nin: ['Furniture', 'Clothing'] }
});
// โ [{ id: 1, category: 'Electronics' }, { id: 3, category: 'Books' }]
// Exclude multiple statuses
filter(items, {
status: { $nin: [404, 500] }
});
// โ Only successful responses$contains - Array Contains Value โ
Checks if an array property contains a specific value.
const products = [
{ name: 'Laptop', tags: ['computer', 'portable', 'gaming'] },
{ name: 'Mouse', tags: ['computer', 'accessory'] },
{ name: 'Desk', tags: ['office', 'furniture'] }
];
filter(products, {
tags: { $contains: 'computer' }
});
// โ [{ name: 'Laptop', ... }, { name: 'Mouse', ... }]
filter(products, {
tags: { $contains: 'gaming' }
});
// โ [{ name: 'Laptop', ... }]$size - Array Length โ
Returns items where the array has a specific length.
filter(products, {
tags: { $size: 2 }
});
// โ [{ name: 'Mouse', tags: ['computer', 'accessory'] }]
filter(products, {
tags: { $size: 3 }
});
// โ [{ name: 'Laptop', tags: ['computer', 'portable', 'gaming'] }]
// Find items with no tags
const allProducts = [
...products,
{ name: 'Unknown', tags: [] }
];
filter(allProducts, { tags: { $size: 0 } });
// โ [{ name: 'Unknown', tags: [] }]String Operators โ
All string operators are case-insensitive by default (configurable).
$startsWith - Starts With โ
const users = [
{ name: 'Alice', email: 'alice@gmail.com' },
{ name: 'Bob', email: 'bob@yahoo.com' },
{ name: 'Alex', email: 'alex@gmail.com' }
];
filter(users, { name: { $startsWith: 'Al' } });
// โ [{ name: 'Alice', ... }, { name: 'Alex', ... }]
// Email domain filtering
filter(users, {
email: { $startsWith: 'alice' }
});
// โ [{ name: 'Alice', ... }]
// Case-sensitive
filter(users, {
name: { $startsWith: 'al' }
}, { caseSensitive: true });
// โ [] (no match)$endsWith - Ends With โ
filter(users, {
email: { $endsWith: 'gmail.com' }
});
// โ [{ name: 'Alice', ... }, { name: 'Alex', ... }]
// File extensions
const files = [
{ name: 'document.pdf' },
{ name: 'image.jpg' },
{ name: 'video.mp4' }
];
filter(files, {
name: { $endsWith: '.pdf' }
});
// โ [{ name: 'document.pdf' }]
filter(files, {
name: { $endsWith: '.jpg' }
});
// โ [{ name: 'image.jpg' }]$contains - String Contains (Substring) โ
filter(users, {
email: { $contains: 'gmail' }
});
// โ [{ name: 'Alice', ... }, { name: 'Alex', ... }]
filter(users, {
name: { $contains: 'li' }
});
// โ [{ name: 'Alice', ... }]
// URL filtering
const links = [
{ url: 'https://example.com/api/users' },
{ url: 'https://example.com/admin/users' },
{ url: 'https://example.com/api/products' }
];
filter(links, {
url: { $contains: '/api/' }
});
// โ API endpoints onlyRegex Operators โ
New in v5.2.0 - Pattern matching with regular expressions.
$regex - Regular Expression Match โ
Match strings using regular expressions.
const users = [
{ email: 'user@gmail.com' },
{ email: 'admin@company.com' },
{ email: 'test@GMAIL.com' }
];
// Case-insensitive email matching
filter(users, {
email: { $regex: /@gmail\.com$/i }
});
// โ [{ email: 'user@gmail.com' }, { email: 'test@GMAIL.com' }]
// Pattern validation
const products = [
{ sku: 'PROD-001-A' },
{ sku: 'PROD-002-B' },
{ sku: 'SERV-001-A' }
];
filter(products, {
sku: { $regex: /^PROD-\d{3}-[A-Z]$/ }
});
// โ [{ sku: 'PROD-001-A' }, { sku: 'PROD-002-B' }]
// Phone number validation
const contacts = [
{ phone: '+1-555-0100' },
{ phone: '555-0200' },
{ phone: '+1-555-0300' }
];
filter(contacts, {
phone: { $regex: /^\+1-\d{3}-\d{4}$/ }
});
// โ [{ phone: '+1-555-0100' }, { phone: '+1-555-0300' }]$match - Alias for $regex โ
$match is an alias for $regex for more intuitive naming:
// These are equivalent:
filter(users, { email: { $regex: /^admin/ } });
filter(users, { email: { $match: /^admin/ } });Complex Patterns โ
// URL validation
const links = [
{ url: 'https://example.com/api/v1/users' },
{ url: 'http://test.com/admin' },
{ url: 'https://example.com/api/v2/products' }
];
filter(links, {
url: { $regex: /^https:\/\/.*\/api\/v[12]\// }
});
// โ API v1 and v2 endpoints with HTTPS
// Date format matching (YYYY-MM-DD)
const records = [
{ date: '2025-01-15' },
{ date: '01/15/2025' },
{ date: '2025-02-20' }
];
filter(records, {
date: { $regex: /^\d{4}-\d{2}-\d{2}$/ }
});
// โ [{ date: '2025-01-15' }, { date: '2025-02-20' }]Performance Note
Regex operators are powerful but slower than simple string operators. Use $startsWith, $endsWith, or $contains when possible for better performance.
// โ ๏ธ Slower: Regex
filter(users, { email: { $regex: /@gmail\.com$/ } });
// โ
Faster: String operator
filter(users, { email: { $endsWith: '@gmail.com' } });Logical Operators โ
New in v5.2.0 - Combine multiple conditions with logical operators for complex queries.
$and - All Conditions Must Match โ
const products = [
{ name: 'Laptop', price: 1200, rating: 4.5, inStock: true },
{ name: 'Mouse', price: 25, rating: 4.0, inStock: true },
{ name: 'Monitor', price: 450, rating: 4.7, inStock: false }
];
// Find products that are affordable AND highly rated AND in stock
filter(products, {
$and: [
{ price: { $lte: 500 } },
{ rating: { $gte: 4.5 } },
{ inStock: true }
]
});
// โ [] (Monitor is not in stock, Mouse rating is 4.0)Implicit AND
Multiple properties at the same level use implicit AND logic:
// These are equivalent:
filter(products, { price: { $lte: 500 }, inStock: true });
filter(products, { $and: [{ price: { $lte: 500 } }, { inStock: true }] });$or - Any Condition Must Match โ
const users = [
{ name: 'Alice', age: 17, isPremium: false },
{ name: 'Bob', age: 25, isPremium: true },
{ name: 'Charlie', age: 30, isPremium: false }
];
// Find users who are either adults OR premium members
filter(users, {
$or: [
{ age: { $gte: 18 } },
{ isPremium: true }
]
});
// โ [{ name: 'Bob', ... }, { name: 'Charlie', ... }]$not - Negation โ
// Find products that are NOT out of stock
filter(products, {
$not: {
inStock: false
}
});
// โ [{ name: 'Laptop', ... }, { name: 'Mouse', ... }]
// Complex negation: NOT (cheap OR low-rated)
filter(products, {
$not: {
$or: [
{ price: { $lt: 100 } },
{ rating: { $lt: 4.0 } }
]
}
});
// โ [{ name: 'Laptop', ... }, { name: 'Monitor', ... }]Combining Logical Operators โ
// Complex e-commerce query:
// (High rating OR popular brand) AND in stock AND affordable
filter(products, {
$and: [
{
$or: [
{ rating: { $gte: 4.5 } },
{ brand: { $in: ['Apple', 'Samsung', 'Sony'] } }
]
},
{ inStock: true },
{ price: { $lte: 1000 } }
]
});
// Clearance items: On sale BUT NOT (out of stock OR discontinued)
filter(products, {
discount: { $gt: 0 },
$not: {
$or: [
{ inStock: false },
{ tags: { $contains: 'discontinued' } }
]
}
});Nested Logical Operators โ
// Find users in specific age ranges with certain conditions
filter(users, {
$or: [
{
$and: [
{ age: { $gte: 18, $lt: 25 } },
{ status: 'student' }
]
},
{
$and: [
{ age: { $gte: 25, $lt: 65 } },
{ status: 'employed' }
]
},
{
$and: [
{ age: { $gte: 65 } },
{ status: 'retired' }
]
}
]
});Combining Operators โ
Multiple Operators on Same Property โ
const products = [
{ name: 'Item A', price: 50 },
{ name: 'Item B', price: 150 },
{ name: 'Item C', price: 250 },
{ name: 'Item D', price: 300 }
];
// Price between 100 and 200, excluding 150
filter(products, {
price: {
$gte: 100,
$lte: 200,
$ne: 150
}
});
// โ [] (Item B is excluded by $ne)
// More realistic: 100-300 range, excluding 250
filter(products, {
price: {
$gte: 100,
$lte: 300,
$ne: 250
}
});
// โ [{ name: 'Item B', ... }, { name: 'Item D', ... }]Multiple Operators on Different Properties โ
const products = [
{ name: 'Laptop Pro', price: 1200, category: 'Electronics', rating: 4.5 },
{ name: 'Mouse', price: 25, category: 'Accessories', rating: 4.0 },
{ name: 'Monitor', price: 450, category: 'Electronics', rating: 4.7 },
{ name: 'Desk', price: 350, category: 'Furniture', rating: 4.2 }
];
// Complex multi-condition filter
filter(products, {
price: { $gte: 100, $lte: 500 },
category: { $in: ['Electronics', 'Accessories'] },
name: { $startsWith: 'M' },
rating: { $gte: 4.0 }
});
// โ [{ name: 'Monitor', ... }]
// E-commerce: Affordable electronics in stock
const inventory = [
{ name: 'Product A', price: 299, category: 'Electronics', inStock: true },
{ name: 'Product B', price: 599, category: 'Electronics', inStock: true },
{ name: 'Product C', price: 199, category: 'Electronics', inStock: false }
];
filter(inventory, {
price: { $lte: 400 },
category: { $eq: 'Electronics' },
inStock: { $eq: true }
});
// โ [{ name: 'Product A', ... }]Intelligent Autocomplete โ
New in v5.2.4 - TypeScript provides intelligent, type-aware operator suggestions that improve developer experience and prevent errors at compile time.
Overview โ
The type system analyzes each property of your interface and suggests only valid operators for that specific type. No more guessing which operators work with which data types!
Type-Aware Suggestions โ
When you type in your editor, TypeScript shows only relevant operators:
interface User {
name: string;
age: number;
tags: string[];
isActive: boolean;
createdAt: Date;
}
const users: User[] = [];
// When you type: filter(users, { age: { $
// Press Ctrl+Space (or Cmd+Space on Mac)
// TypeScript shows ONLY:
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// โ โ $gt - Greater than โ
// โ โ $gte - Greater than or equal โ
// โ โ $lt - Less than โ
// โ โ $lte - Less than or equal โ
// โ โ $eq - Equal โ
// โ โ $ne - Not equal โ
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// You WON'T see $startsWith, $contains, etc.
// (they don't make sense for numbers!)Operator Mapping by Type โ
| Type | Available Operators |
|---|---|
string | $startsWith, $endsWith, $contains, $regex, $match, $eq, $ne |
number | $gt, $gte, $lt, $lte, $eq, $ne |
Date | $gt, $gte, $lt, $lte, $eq, $ne |
Array\<T> | $in, $nin, $contains, $size |
boolean | $eq, $ne |
other | $eq, $ne |
Nested Object Autocomplete โ
New in v5.2.4 - Autocomplete works up to 4 levels deep for nested objects:
interface User {
name: string;
address: {
city: string;
coordinates: {
lat: number;
lng: number;
};
};
settings: {
privacy: {
showEmail: boolean;
};
};
}
const users: User[] = [];
// โ
Level 1: Root properties
filter(users, {
name: { $startsWith: 'John' } // Autocompletes string operators
});
// โ
Level 2: Nested objects
filter(users, {
address: {
city: { $startsWith: 'New' } // Autocompletes string operators
}
});
// โ
Level 3: Deeply nested objects
filter(users, {
address: {
coordinates: {
lat: { $gte: -90, $lte: 90 } // Autocompletes number operators
}
}
});
// โ
Level 4: Very deeply nested objects
filter(users, {
settings: {
privacy: {
showEmail: { $eq: true } // Autocompletes boolean operators
}
}
});Type Safety โ
TypeScript prevents errors at compile time:
interface User {
age: number;
name: string;
}
// โ Error: $startsWith is not valid for numbers
filter(users, {
age: {
$startsWith: '25' // TypeScript marks this as an error
}
});
// โ
Correct usage
filter(users, {
age: { $gte: 25 }, // Number operators
name: { $startsWith: 'John' } // String operators
});Editor Support โ
Autocomplete works in any editor with TypeScript support:
- VS Code/Cursor: Press
Ctrl+Space(Windows/Linux) orCmd+Space(Mac) - WebStorm/IntelliJ: Autocomplete appears automatically
- Vim/Neovim: With LSP configured (coc.nvim, nvim-lsp)
- Sublime Text: With LSP-typescript
- Emacs: With lsp-mode or eglot
Benefits โ
- Discovery: Discover available operators without consulting documentation
- Error Prevention: TypeScript prevents the use of incorrect operators
- Productivity: Write code faster with intelligent suggestions
- Maintainability: Code is easier to understand and maintain
- Safe Refactoring: Type changes propagate automatically
- Zero Cost: Pure TypeScript feature, no runtime overhead
Lazy Evaluation โ
New in v5.1.0 - Process large datasets efficiently with generators and lazy evaluation. Stop processing early and save resources.
Overview โ
Lazy evaluation allows you to process items on-demand rather than filtering the entire array upfront. This approach:
- 500x faster for early exits (finding first N matches)
- Reduces memory footprint - Only processes items as needed
- Improves performance - Stops early when conditions are met
- Enables infinite streams - Works with generators and async iterables
- Supports chunked processing - Process data in manageable batches
filterLazy โ
Returns a lazy iterator that yields filtered items on-demand.
Signature:
function filterLazy<T>(
iterable: Iterable<T>,
expression: Expression<T>,
options?: FilterOptions
): Generator<T, void, undefined>Example:
import { filterLazy } from '@mcabreradev/filter';
const millionRecords = [/* 1,000,000 items */];
// Lazy evaluation - items processed on-demand
const filtered = filterLazy(millionRecords, {
status: 'active',
score: { $gte: 90 }
});
// Consume only what you need
for (const item of filtered) {
console.log(item);
if (someCondition) break; // Stop early - remaining items never processed!
}filterFirst โ
Find the first N matching items and stop processing.
Signature:
function filterFirst<T>(
iterable: Iterable<T>,
expression: Expression<T>,
limit: number,
options?: FilterOptions
): T[]Example:
import { filterFirst } from '@mcabreradev/filter';
// Find first 10 high-value transactions
const topTransactions = filterFirst(
millionTransactions,
{ amount: { $gte: 10000 }, status: 'completed' },
10 // Stop after finding 10 matches
);
// โ 500x faster than filtering entire array!Performance Benefits โ
Performance Comparison โ
const largeDataset = Array.from({ length: 1_000_000 }, (_, i) => ({
id: i,
value: Math.random() * 1000,
status: i % 2 === 0 ? 'active' : 'inactive'
}));
// โ Slow: Process entire array
console.time('eager');
const eager = filter(largeDataset, { status: 'active', value: { $gte: 900 } });
const first10Eager = eager.slice(0, 10);
console.timeEnd('eager'); // ~250ms
// โ
Fast: Stop after finding 10
console.time('lazy');
const first10Lazy = filterFirst(
largeDataset,
{ status: 'active', value: { $gte: 900 } },
10
);
console.timeEnd('lazy'); // ~0.5ms (500x faster!)Real-World Use Cases โ
Pagination โ
function getPage(
data: Item[],
page: number,
pageSize: number,
filters: Expression<Item>
) {
const skip = page * pageSize;
const lazy = filterLazy(data, filters);
let count = 0;
const results: Item[] = [];
for (const item of lazy) {
if (count >= skip && results.length < pageSize) {
results.push(item);
}
count++;
if (results.length === pageSize) break; // Stop when page is full
}
return results;
}Search with Limit โ
function searchProducts(query: string, limit: number = 20) {
return filterFirst(
products,
{ name: { $contains: query }, inStock: true },
limit
);
}Combining with Caching โ
// Lazy evaluation + caching for repeated queries
const results = filterFirst(
largeDataset,
{ category: 'Electronics', price: { $lte: 1000 } },
50,
{ enableCache: true }
);
// First run: ~15ms (early exit)
// Cached run: ~0.01ms (instant!)Performance Metrics โ
| Operation | Eager Filter | Lazy Filter | Speedup |
|---|---|---|---|
| Find first 10 (1M items) | 250ms | 0.5ms | 500x |
| Find first 100 (1M items) | 250ms | 5ms | 50x |
| Process until condition | 250ms | Variable | 10-500x |
| Memory usage (1M items) | 80MB | <1MB | 80x less |
Configuration API โ
Customize filter behavior with configuration options.
caseSensitive โ
Controls case sensitivity for string matching.
Type: booleanDefault: false
const users = [
{ name: 'Alice' },
{ name: 'ALICE' },
{ name: 'alice' }
];
// Case-insensitive (default)
filter(users, 'alice');
// โ All three users
filter(users, 'ALICE');
// โ All three users
// Case-sensitive
filter(users, 'alice', { caseSensitive: true });
// โ [{ name: 'alice' }]
filter(users, 'ALICE', { caseSensitive: true });
// โ [{ name: 'ALICE' }]Impact on String Operators:
const emails = [
{ email: 'User@Example.com' },
{ email: 'admin@example.com' }
];
// Case-insensitive
filter(emails, {
email: { $startsWith: 'user' }
});
// โ [{ email: 'User@Example.com' }]
// Case-sensitive
filter(emails, {
email: { $startsWith: 'user' }
}, { caseSensitive: true });
// โ [] (no match)maxDepth โ
Sets maximum depth for nested object comparison.
Type: numberDefault: 3Range: 1-10
const data = [
{
level1: {
level2: {
level3: {
level4: {
value: 'deep'
}
}
}
}
}
];
// Default depth (3)
filter(data, {
level1: {
level2: {
level3: { value: 'deep' }
}
}
});
// โ May not match if depth exceeded
// Increase depth
filter(data, {
level1: {
level2: {
level3: {
level4: { value: 'deep' }
}
}
}
}, { maxDepth: 5 });
// โ MatchesPerformance Note: Higher depth values may impact performance with deeply nested objects.
enableCache โ
Enables multi-layer memoization for maximum performance.
Type: booleanDefault: falseNew in: v5.2.0 (enhanced implementation)
const largeDataset = [/* 10,000 items */];
// Without cache
const result1 = filter(largeDataset, { category: 'Electronics' });
const result2 = filter(largeDataset, { category: 'Electronics' }); // Recalculates
// With cache
const result1 = filter(largeDataset, { category: 'Electronics' }, { enableCache: true });
const result2 = filter(largeDataset, { category: 'Electronics' }, { enableCache: true });
// โ Second call is 530x faster! โกCache Layers:
- Result Cache - Complete filter results (WeakMap)
- Predicate Cache - Compiled predicates (LRU + TTL)
- Regex Cache - Compiled patterns (Map)
When to Enable:
- โ Large datasets (>1,000 items)
- โ Repeated identical queries
- โ Complex expressions with regex
- โ Read-heavy workloads
- โ Dashboard/analytics views
When NOT to Enable:
- โ Frequently changing data
- โ One-time queries
- โ Memory-constrained environments
- โ Unique expressions every time
Performance Impact:
// Benchmark: 10,000 items
const products = [/* 10,000 products */];
const query = { price: { $gte: 100, $lte: 500 }, inStock: true };
// First run
console.time('first');
filter(products, query, { enableCache: true });
console.timeEnd('first'); // ~5.3ms
// Second run (cached)
console.time('cached');
filter(products, query, { enableCache: true });
console.timeEnd('cached'); // ~0.01ms (530x faster!)Cache Management:
import { filter, clearFilterCache, getFilterCacheStats } from '@mcabreradev/filter';
// Get cache statistics
const stats = getFilterCacheStats();
console.log(stats);
// โ { predicateCacheSize: 45, regexCacheSize: 12 }
// Clear all caches
clearFilterCache();
// Automatic cleanup with WeakMap
let data = [/* large dataset */];
filter(data, query, { enableCache: true });
data = null; // Cache automatically cleared โจReal-World Example:
class ProductService {
private products: Product[];
constructor(products: Product[]) {
this.products = products;
}
// All methods use caching
getElectronics() {
return filter(
this.products,
{ category: 'Electronics' },
{ enableCache: true }
);
}
getInStock() {
return filter(
this.products,
{ inStock: true },
{ enableCache: true }
);
}
// Clear cache when data updates
refreshProducts(newProducts: Product[]) {
this.products = newProducts;
clearFilterCache();
}
}
const service = new ProductService(products);
// First calls: normal speed
service.getElectronics(); // 5.3ms
service.getInStock(); // 4.8ms
// User navigates between views...
// Subsequent calls: instant!
service.getElectronics(); // 0.01ms โก
service.getInStock(); // 0.01ms โกAdvanced: Combining with Lazy Evaluation:
import { filterFirst } from '@mcabreradev/filter';
// Find first 10 matches with caching
const topResults = filterFirst(
millionRecords,
{ status: 'active', score: { $gte: 90 } },
10,
{ enableCache: true }
);
// First run: ~15ms (early exit)
// Cached run: ~0.01ms (instant!)Memory Usage:
| Cache Type | Memory per Entry | Max Memory |
|---|---|---|
| Result Cache | ~8 bytes | Unlimited* |
| Predicate Cache | ~200 bytes | ~100 KB |
| Regex Cache | ~150 bytes | ~150 KB |
*WeakMap automatically garbage collects
See also:
- Memoization Guide - Complete caching documentation
- Performance Benchmarks - Detailed benchmarks
customComparator โ
Provide custom comparison logic.
Type: (actual: unknown, expected: unknown) => booleanDefault: Case-insensitive substring matching
// Custom exact match comparator
const exactMatch = (actual: unknown, expected: unknown) => actual === expected;
filter(users, 'Alice', { customComparator: exactMatch });
// โ Exact matches only
// Custom numeric comparator with tolerance
const numericTolerance = (actual: unknown, expected: unknown) => {
if (typeof actual === 'number' && typeof expected === 'number') {
return Math.abs(actual - expected) < 0.01;
}
return actual === expected;
};
const measurements = [
{ value: 10.001 },
{ value: 10.002 },
{ value: 11.0 }
];
filter(measurements, 10, { customComparator: numericTolerance });
// โ [{ value: 10.001 }, { value: 10.002 }]
// Custom locale-aware string comparator
const localeCompare = (actual: unknown, expected: unknown) => {
if (typeof actual === 'string' && typeof expected === 'string') {
return actual.localeCompare(expected, 'en', { sensitivity: 'base' }) === 0;
}
return actual === expected;
};Mixing Syntax Patterns โ
Combine different filtering syntaxes for maximum flexibility.
Operators + Simple Equality โ
const products = [
{ name: 'Laptop', price: 1200, category: 'Electronics' },
{ name: 'Monitor', price: 450, category: 'Electronics' },
{ name: 'Desk', price: 350, category: 'Furniture' }
];
filter(products, {
category: 'Electronics', // Simple equality
price: { $gte: 400 } // Operator
});
// โ [{ name: 'Laptop', ... }, { name: 'Monitor', ... }]Operators + Wildcards โ
const files = [
{ name: 'report-2025.pdf', size: 1024 },
{ name: 'image-2025.jpg', size: 2048 },
{ name: 'video-2025.mp4', size: 10240 }
];
filter(files, {
name: '%2025%', // Wildcard
size: { $lt: 5000 } // Operator
});
// โ [{ name: 'report-2025.pdf', ... }, { name: 'image-2025.jpg', ... }]Operators + Negation โ
const users = [
{ name: 'Alice', role: 'admin', age: 30 },
{ name: 'Bob', role: 'user', age: 25 },
{ name: 'Charlie', role: 'admin', age: 35 }
];
filter(users, {
role: '!user', // Negation
age: { $gte: 30 } // Operator
});
// โ [{ name: 'Alice', ... }, { name: 'Charlie', ... }]Complex Combination โ
const products = [
{
name: 'Gaming Laptop',
price: 1500,
category: 'Electronics',
tags: ['gaming', 'portable'],
inStock: true
},
{
name: 'Office Laptop',
price: 800,
category: 'Electronics',
tags: ['business', 'portable'],
inStock: true
},
{
name: 'Gaming Desktop',
price: 2000,
category: 'Electronics',
tags: ['gaming', 'powerful'],
inStock: false
}
];
filter(products, {
category: 'Electronics', // Simple equality
name: '%Laptop%', // Wildcard
price: { $gte: 700, $lte: 1600 }, // Range operators
tags: { $contains: 'gaming' }, // Array operator
inStock: { $eq: true } // Boolean operator
});
// โ [{ name: 'Gaming Laptop', ... }]TypeScript Integration โ
Full TypeScript support with strict typing.
Type Definitions โ
import type {
Expression,
PrimitiveExpression,
PredicateFunction,
ObjectExpression,
FilterOptions,
FilterConfig,
Comparator,
ComparisonOperators,
ArrayOperators,
StringOperators,
OperatorExpression
} from '@mcabreradev/filter';Generic Type Parameter โ
interface Product {
id: number;
name: string;
price: number;
category: string;
tags: string[];
inStock: boolean;
}
const products: Product[] = [
{ id: 1, name: 'Laptop', price: 1200, category: 'Electronics', tags: ['computer'], inStock: true },
// ...
];
// Type-safe filtering
const result = filter<Product>(products, {
price: { $gte: 100 }
});
// result is Product[]
// TypeScript catches errors
filter<Product>(products, {
price: { $gte: '100' } // โ Error: Type 'string' not assignable to 'number | Date'
});
filter<Product>(products, {
invalidProp: 'value' // โ Error: Property 'invalidProp' does not exist
});Type Inference โ
// TypeScript infers types automatically
const numbers = [1, 2, 3, 4, 5];
const filtered = filter(numbers, (n) => n > 3);
// filtered is number[]
const users = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 }
];
const adults = filter(users, (user) => user.age >= 18);
// adults is { name: string; age: number; }[]Custom Types with Operators โ
interface User {
id: number;
name: string;
email: string;
age: number;
roles: string[];
createdAt: Date;
}
// Type-safe operator expressions
const ageFilter: ComparisonOperators = {
$gte: 18,
$lte: 65
};
const roleFilter: ArrayOperators = {
$contains: 'admin'
};
const emailFilter: StringOperators = {
$endsWith: '@company.com'
};
// Combine in filter
filter<User>(users, {
age: ageFilter,
roles: roleFilter,
email: emailFilter
});Predicate Type Safety โ
interface Product {
id: number;
name: string;
price: number;
}
const products: Product[] = [/* ... */];
// Fully typed predicate
filter<Product>(products, (product: Product): boolean => {
// TypeScript provides autocomplete and type checking
return product.price > 100 && product.name.includes('Pro');
});
// Arrow function with implicit return
filter<Product>(products, (p) => p.price > 100);Expression Type โ
// Define reusable expressions
const electronicFilter: Expression<Product> = {
category: 'Electronics',
price: { $lte: 1000 }
};
const premiumFilter: Expression<Product> = (product) =>
product.price > 500 && product.rating >= 4.5;
// Use in filters
filter<Product>(products, electronicFilter);
filter<Product>(products, premiumFilter);FilterOptions Type โ
const options: FilterOptions = {
caseSensitive: true,
maxDepth: 5,
enableCache: false
};
filter(data, expression, options);
// Partial options
const partialOptions: FilterOptions = {
caseSensitive: true
// Other options use defaults
};Real-World Examples โ
Comprehensive examples for common use cases.
E-commerce Product Filtering โ
interface Product {
id: number;
name: string;
price: number;
category: string;
subcategory: string;
brand: string;
rating: number;
reviews: number;
inStock: boolean;
tags: string[];
discount: number;
images: string[];
createdAt: Date;
}
const products: Product[] = [
{
id: 1,
name: 'Dell XPS 15 Laptop',
price: 1499,
category: 'Electronics',
subcategory: 'Computers',
brand: 'Dell',
rating: 4.7,
reviews: 1250,
inStock: true,
tags: ['laptop', 'gaming', 'professional'],
discount: 10,
images: ['img1.jpg', 'img2.jpg'],
createdAt: new Date('2025-01-15')
},
// ... more products
];
// Find affordable laptops with good ratings
const affordableLaptops = filter(products, {
category: 'Electronics',
subcategory: 'Computers',
price: { $lte: 1500 },
rating: { $gte: 4.5 },
inStock: { $eq: true }
});
// Products on sale (discount > 0)
const onSale = filter(products, {
discount: { $gt: 0 },
inStock: true
});
// New arrivals (last 30 days)
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const newArrivals = filter(products, {
createdAt: { $gte: thirtyDaysAgo },
inStock: true
});
// Premium products by specific brands
const premiumProducts = filter(products, {
brand: { $in: ['Apple', 'Dell', 'HP'] },
price: { $gte: 1000 },
rating: { $gte: 4.5 }
});
// Products with many reviews
const popularProducts = filter(products, {
reviews: { $gte: 1000 },
rating: { $gte: 4.0 }
});
// Search by keyword in name
const searchResults = filter(products, {
name: { $contains: 'laptop' },
price: { $lte: 2000 }
});User Management System โ
interface User {
id: number;
username: string;
email: string;
age: number;
roles: string[];
isActive: boolean;
department: string;
salary: number;
hireDate: Date;
lastLogin: Date;
}
const users: User[] = [/* ... */];
// Active admin users
const activeAdmins = filter(users, {
roles: { $contains: 'admin' },
isActive: { $eq: true }
});
// Users hired this year
const thisYear = new Date().getFullYear();
const newHires = filter(users, {
hireDate: { $gte: new Date(`${thisYear}-01-01`) }
});
// High-salary employees in Engineering
const topEngineers = filter(users, {
department: 'Engineering',
salary: { $gte: 100000 }
});
// Inactive users (no login in 90 days)
const ninetyDaysAgo = new Date();
ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
const inactiveUsers = filter(users, {
lastLogin: { $lt: ninetyDaysAgo },
isActive: true
});
// Users by email domain
const companyUsers = filter(users, {
email: { $endsWith: '@company.com' }
});
// Users with multiple roles
const multiRoleUsers = filter(users, {
roles: { $size: { $gte: 2 } }
});
// Adult users in specific departments
const eligibleUsers = filter(users, {
age: { $gte: 18, $lte: 65 },
department: { $in: ['Engineering', 'Sales', 'Marketing'] },
isActive: true
});Data Analytics & Reporting โ
interface Transaction {
id: string;
amount: number;
currency: string;
status: 'pending' | 'completed' | 'failed' | 'refunded';
customerId: string;
date: Date;
category: string;
paymentMethod: string;
country: string;
}
const transactions: Transaction[] = [/* ... */];
// Successful transactions above threshold
const highValueTransactions = filter(transactions, {
amount: { $gte: 10000 },
status: 'completed'
});
// Failed transactions for investigation
const failedPayments = filter(transactions, {
status: 'failed',
date: {
$gte: new Date('2025-01-01'),
$lte: new Date('2025-12-31')
}
});
// International transactions
const internationalSales = filter(transactions, {
country: { $nin: ['USA', 'US'] },
status: 'completed'
});
// Refund analysis
const refunds = filter(transactions, {
status: 'refunded',
amount: { $gte: 100 }
});
// Payment method distribution
const cardPayments = filter(transactions, {
paymentMethod: { $startsWith: 'card' },
status: { $in: ['completed', 'pending'] }
});
// Monthly revenue (Q1 example)
const q1Revenue = filter(transactions, {
date: {
$gte: new Date('2025-01-01'),
$lte: new Date('2025-03-31')
},
status: 'completed'
});
const totalRevenue = q1Revenue.reduce((sum, t) => sum + t.amount, 0);Search Functionality โ
interface Article {
id: number;
title: string;
content: string;
author: string;
tags: string[];
category: string;
publishDate: Date;
views: number;
likes: number;
status: 'draft' | 'published' | 'archived';
}
const articles: Article[] = [/* ... */];
// Full-text search (title + content)
const searchTerm = 'typescript';
const searchResults = filter(articles, (article) =>
article.title.toLowerCase().includes(searchTerm) ||
article.content.toLowerCase().includes(searchTerm)
);
// Advanced search with filters
const advancedSearch = filter(articles, {
title: { $contains: 'typescript' },
category: { $in: ['Technology', 'Programming'] },
status: 'published',
publishDate: { $gte: new Date('2025-01-01') },
views: { $gte: 1000 }
});
// Popular articles
const popularArticles = filter(articles, {
views: { $gte: 10000 },
likes: { $gte: 500 },
status: 'published'
});
// Articles by tag
const taggedArticles = filter(articles, {
tags: { $contains: 'tutorial' },
status: 'published'
});
// Recent articles by author
const authorArticles = filter(articles, {
author: { $startsWith: 'John' },
publishDate: { $gte: new Date('2025-01-01') },
status: 'published'
});Inventory Management โ
interface InventoryItem {
sku: string;
name: string;
quantity: number;
reorderLevel: number;
price: number;
category: string;
supplier: string;
expiryDate: Date | null;
location: string;
tags: string[];
}
const inventory: InventoryItem[] = [/* ... */];
// Low stock alerts
const lowStock = filter(inventory, (item) =>
item.quantity <= item.reorderLevel
);
// Items needing reorder
const needsReorder = filter(inventory, {
quantity: { $lte: 10 },
reorderLevel: { $gte: 10 }
});
// Expiring items (next 30 days)
const thirtyDaysFromNow = new Date();
thirtyDaysFromNow.setDate(thirtyDaysFromNow.getDate() + 30);
const expiringItems = filter(inventory, (item) =>
item.expiryDate !== null &&
item.expiryDate <= thirtyDaysFromNow &&
item.quantity > 0
);
// High-value inventory
const highValueItems = filter(inventory, {
price: { $gte: 1000 },
quantity: { $gte: 1 }
});
const totalValue = highValueItems.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);
// Items by supplier
const supplierItems = filter(inventory, {
supplier: { $in: ['Supplier A', 'Supplier B'] },
quantity: { $gt: 0 }
});
// Items by location and category
const warehouseElectronics = filter(inventory, {
location: { $startsWith: 'Warehouse' },
category: 'Electronics',
quantity: { $gte: 1 }
});Performance Optimization โ
Tips and strategies for optimal performance.
When to Use Each Pattern โ
Performance Ranking (fastest to slowest):
Simple String/Number/Boolean - Fastest
typescriptfilter(data, 'Berlin'); // โ FastestObject-Based Filtering - Very Fast
typescriptfilter(data, { city: 'Berlin' }); // โ Very FastOperators - Fast (optimized with early exit)
typescriptfilter(data, { age: { $gte: 18 } }); // โ FastWildcards - Moderate (regex compilation cached)
typescriptfilter(data, '%berlin%'); // โ ๏ธ ModeratePredicate Functions - Slowest (most flexible)
typescriptfilter(data, (item) => item.age > 18); // โ ๏ธ Slowest
Caching Strategy โ
const largeDataset = [/* ... */]; // 100,000 items
// โ Without cache: Re-computes every time
for (let i = 0; i < 1000; i++) {
filter(largeDataset, { category: 'Electronics' });
}
// โ
With cache: Computes once, reuses result
for (let i = 0; i < 1000; i++) {
filter(largeDataset, { category: 'Electronics' }, { enableCache: true });
}When to Enable Cache:
- Large datasets (>1000 items)
- Repeated identical queries
- Read-heavy workloads
- Static data
When NOT to Enable:
- Frequently changing data
- One-time queries
- Memory-constrained environments
Large Dataset Handling โ
// โ Slow: Processing entire dataset
const results = filter(millionRecords, complexExpression);
displayResults(results);
// โ
Better: Pagination
const pageSize = 100;
const page = 0;
const pagedData = millionRecords.slice(page * pageSize, (page + 1) * pageSize);
const results = filter(pagedData, expression);
// โ
Best: Pre-filter with operators, then apply complex logic
const preFiltered = filter(millionRecords, {
category: { $in: ['Electronics', 'Books'] } // Fast operator filter
});
const finalResults = filter(preFiltered, complexPredicate); // Smaller datasetOptimization Tips โ
// โ Slow: Complex predicate on large dataset
filter(largeDataset, (item) => {
const calculated = item.price * 1.2 + item.tax;
return calculated > 100 &&
item.category.toLowerCase().includes('electronics') &&
item.inStock &&
item.rating >= 4.0;
});
// โ
Fast: Use operators when possible
filter(largeDataset, {
price: { $gte: 80 }, // Pre-calculated threshold
category: { $contains: 'electronics' },
inStock: { $eq: true },
rating: { $gte: 4.0 }
});
// โ
Fast: Pre-filter with simple checks first
const preFiltered = filter(largeDataset, { inStock: true });
const results = filter(preFiltered, complexExpression);Benchmarking โ
// Measure filter performance
console.time('Filter Performance');
const results = filter(data, expression, options);
console.timeEnd('Filter Performance');
// Compare different approaches
const testData = Array.from({ length: 10000 }, (_, i) => ({
id: i,
value: Math.random() * 1000,
category: i % 2 === 0 ? 'A' : 'B'
}));
console.time('Simple Object');
filter(testData, { category: 'A' });
console.timeEnd('Simple Object');
console.time('Operator');
filter(testData, { value: { $gte: 500 } });
console.timeEnd('Operator');
console.time('Predicate');
filter(testData, (item) => item.value >= 500);
console.timeEnd('Predicate');Migration Guides โ
Migrating to v5.0.0 โ
From v3.x:
โ 100% Backward Compatible - No breaking changes!
All v3.x code continues to work:
// โ
v3.x syntax still works in v5.0.0
filter(data, 'string');
filter(data, { prop: 'value' });
filter(data, (item) => true);
filter(data, '%pattern%');New Features to Adopt:
// โ
v5.0.0: MongoDB-style operators
filter(data, { age: { $gte: 18, $lt: 65 } });
// โ
v5.0.0: Configuration options
filter(data, expression, {
caseSensitive: true,
maxDepth: 5,
enableCache: true
});
// โ
v5.0.0: Runtime validation
import { validateExpression, validateOptions } from '@mcabreradev/filter';Recommended Updates:
// Before (v3.x) - Still works
filter(products, (p) => p.price >= 100 && p.price <= 500);
// After (v5.0.0) - Recommended
filter(products, {
price: { $gte: 100, $lte: 500 }
});
// Benefits:
// - More declarative and readable
// - Can be serialized to JSON
// - Better TypeScript support
// - Runtime validationFrom Native Array.filter() โ
Direct Replacements:
// Native filter
const adults = users.filter(u => u.age >= 18);
// @mcabreradev/filter equivalent
const adults = filter(users, (u) => u.age >= 18);
// OR better with operators:
const adults = filter(users, { age: { $gte: 18 } });
// Native: Multiple conditions
const results = users.filter(u =>
u.age >= 18 &&
u.city === 'Berlin' &&
u.isActive
);
// @mcabreradev/filter
const results = filter(users, {
age: { $gte: 18 },
city: 'Berlin',
isActive: true
});Enhanced Capabilities:
// โ Native: No wildcard support
const results = users.filter(u =>
u.email.includes('gmail.com')
);
// โ
@mcabreradev/filter
const results = filter(users, {
email: { $endsWith: 'gmail.com' }
});
// OR
const results = filter(users, '%gmail.com');
// โ Native: Complex string operations
const results = users.filter(u =>
u.name.toLowerCase().startsWith('al')
);
// โ
@mcabreradev/filter
const results = filter(users, {
name: { $startsWith: 'Al' }
});When to Migrate:
Migrate When:
- Need wildcard pattern matching
- Filtering by partial object matches
- Want to serialize filter expressions
- Need case-insensitive search
- Want MongoDB-style query syntax
- Need deep object comparison
Keep Native When:
- Simple one-time predicates
- Performance is ultra-critical (native is marginally faster)
- No dependencies requirement is strict
- Project already has comprehensive native filter logic
Validation & Error Handling โ
Runtime validation with Zod.
Expression Validation โ
import { validateExpression } from '@mcabreradev/filter';
// Valid expressions
validateExpression('string'); // โ
Valid
validateExpression(42); // โ
Valid
validateExpression({ prop: 'value' }); // โ
Valid
validateExpression((x) => true); // โ
Valid
// Invalid expressions
try {
validateExpression(undefined); // โ Throws error
} catch (error) {
console.error(error.message);
// "Invalid filter expression: Expected string | number | boolean | null | function | object"
}
try {
validateExpression(Symbol('test')); // โ Throws error
} catch (error) {
console.error(error.message);
}Options Validation โ
import { validateOptions } from '@mcabreradev/filter';
// Valid options
validateOptions({ caseSensitive: true }); // โ
Valid
validateOptions({ maxDepth: 5 }); // โ
Valid
validateOptions({ enableCache: false }); // โ
Valid
validateOptions({}); // โ
Valid (uses defaults)
// Invalid options
try {
validateOptions({ maxDepth: 15 }); // โ maxDepth must be 1-10
} catch (error) {
console.error(error.message);
// "Invalid filter options: maxDepth too large"
}
try {
validateOptions({ caseSensitive: 'yes' }); // โ Wrong type
} catch (error) {
console.error(error.message);
// "Invalid filter options: Expected boolean, received string"
}Common Errors & Solutions โ
Error: "Invalid filter expression" โ
// โ Problem
filter(data, undefined);
filter(data, null);
// โ
Solution
if (expression !== undefined && expression !== null) {
filter(data, expression);
}Error: "Invalid filter options: maxDepth too large" โ
// โ Problem
filter(data, expression, { maxDepth: 15 });
// โ
Solution
filter(data, expression, { maxDepth: 10 }); // Max is 10Error: Unexpected Results โ
// โ Problem: Case sensitivity issue
filter(users, 'BERLIN', { caseSensitive: true });
// Returns [] if all data is lowercase
// โ
Solution: Check case sensitivity
filter(users, 'berlin', { caseSensitive: false }); // DefaultDebug Tips โ
// 1. Validate before filtering
import { validateExpression, validateOptions } from '@mcabreradev/filter';
try {
validateExpression(expression);
validateOptions(options);
const results = filter(data, expression, options);
} catch (error) {
console.error('Filter error:', error.message);
}
// 2. Test with simple data first
const testData = [{ id: 1, name: 'Test' }];
const testResult = filter(testData, expression);
console.log('Test result:', testResult);
// 3. Check expression type
console.log('Expression type:', typeof expression);
console.log('Expression value:', expression);
// 4. Verify data structure
console.log('Data sample:', data[0]);
console.log('Data length:', data.length);API Reference โ
Complete reference for all exported functions and types.
filter<T>(array, expression, options?) โ
Main filtering function.
Signature:
function filter<T>(
array: T[],
expression: Expression<T>,
options?: FilterOptions
): T[]Parameters:
array(T[]): Array to filterexpression(Expression\<T>): Filter expression (string, number, boolean, null, object, or predicate)options(FilterOptions, optional): Configuration options
Returns: T[] - Filtered array
Throws: Error if expression or options are invalid
Examples:
filter([1, 2, 3], 2);
filter(users, { age: 30 });
filter(products, { price: { $gte: 100 } });
filter(items, (item) => item.active);validateExpression(expression) โ
Validates a filter expression.
Signature:
function validateExpression<T>(expression: unknown): Expression<T>Parameters:
expression(unknown): Expression to validate
Returns: Expression\<T> - Validated expression
Throws: Error if expression is invalid
Examples:
validateExpression('string'); // โ
Returns 'string'
validateExpression(undefined); // โ Throws ErrorvalidateOptions(options) โ
Validates filter options.
Signature:
function validateOptions(options: unknown): FilterOptionsParameters:
options(unknown): Options to validate
Returns: FilterOptions - Validated options
Throws: Error if options are invalid
Examples:
validateOptions({ caseSensitive: true }); // โ
Returns options
validateOptions({ maxDepth: 15 }); // โ Throws Error (max is 10)mergeConfig(options) โ
Merges options with defaults.
Signature:
function mergeConfig(options?: FilterOptions): FilterConfigParameters:
options(FilterOptions, optional): User options
Returns: FilterConfig - Complete configuration with defaults
Examples:
mergeConfig(); // Returns default config
mergeConfig({ caseSensitive: true }); // Merges with defaultscreateFilterConfig(options) โ
Creates a complete filter configuration.
Signature:
function createFilterConfig(options?: FilterOptions): FilterConfigParameters:
options(FilterOptions, optional): User options
Returns: FilterConfig - Complete configuration
Examples:
const config = createFilterConfig({ maxDepth: 5 });Type Exports โ
// Expression types
export type Expression<T> =
| PrimitiveExpression
| PredicateFunction<T>
| ObjectExpression<T>
| ExtendedObjectExpression<T>;
export type PrimitiveExpression = string | number | boolean | null;
export type PredicateFunction<T> = (item: T) => boolean;
export type ObjectExpression<T> = Partial<{
[K in keyof T]: T[K] | string;
}>;
// Config types
export interface FilterConfig {
caseSensitive: boolean;
maxDepth: number;
customComparator?: Comparator;
enableCache: boolean;
}
export type FilterOptions = Partial<FilterConfig>;
export type Comparator = (actual: unknown, expected: unknown) => boolean;
// Operator types
export interface ComparisonOperators {
$gt?: number | Date;
$gte?: number | Date;
$lt?: number | Date;
$lte?: number | Date;
$eq?: unknown;
$ne?: unknown;
}
export interface ArrayOperators {
$in?: unknown[];
$nin?: unknown[];
$contains?: unknown;
$size?: number;
}
export interface StringOperators {
$startsWith?: string;
$endsWith?: string;
$contains?: string;
}Testing Your Filters โ
Best practices for testing filter logic.
Unit Testing with Vitest/Jest โ
import { describe, it, expect } from 'vitest'; // or 'jest'
import { filter } from '@mcabreradev/filter';
describe('User Filtering', () => {
const users = [
{ id: 1, name: 'Alice', age: 30, role: 'admin' },
{ id: 2, name: 'Bob', age: 25, role: 'user' },
{ id: 3, name: 'Charlie', age: 35, role: 'user' }
];
it('filters by age', () => {
const result = filter(users, { age: { $gte: 30 } });
expect(result).toHaveLength(2);
expect(result[0].name).toBe('Alice');
expect(result[1].name).toBe('Charlie');
});
it('filters by role', () => {
const result = filter(users, { role: 'admin' });
expect(result).toHaveLength(1);
expect(result[0].name).toBe('Alice');
});
it('handles empty results', () => {
const result = filter(users, { age: { $gt: 100 } });
expect(result).toEqual([]);
});
it('filters with predicates', () => {
const result = filter(users, (u) => u.age > 28);
expect(result).toHaveLength(2);
});
});Mocking Data โ
// Test helpers
function createMockUser(overrides = {}) {
return {
id: 1,
name: 'Test User',
age: 25,
email: 'test@example.com',
isActive: true,
...overrides
};
}
describe('Complex Filters', () => {
it('handles large datasets', () => {
const largeDataset = Array.from({ length: 1000 }, (_, i) =>
createMockUser({ id: i, age: 20 + (i % 50) })
);
const result = filter(largeDataset, { age: { $gte: 30, $lt: 40 } });
expect(result.length).toBeGreaterThan(0);
});
});Edge Cases โ
describe('Edge Cases', () => {
it('handles empty arrays', () => {
const result = filter([], { id: 1 });
expect(result).toEqual([]);
});
it('handles null values', () => {
const data = [
{ id: 1, value: null },
{ id: 2, value: 'test' }
];
const result = filter(data, { value: null });
expect(result).toHaveLength(1);
});
it('handles undefined properties', () => {
const data = [
{ id: 1 },
{ id: 2, value: 'test' }
];
const result = filter(data, { value: 'test' });
expect(result).toHaveLength(1);
});
it('handles special characters', () => {
const data = [
{ email: 'user@example.com' },
{ email: 'admin@test.org' }
];
const result = filter(data, { email: '%@example.com' });
expect(result).toHaveLength(1);
});
});Integration Testing โ
describe('E-commerce Integration', () => {
let products;
beforeEach(() => {
products = loadTestProducts(); // Load test data
});
it('filters products for product listing page', () => {
const results = filter(products, {
category: 'Electronics',
price: { $lte: 1000 },
inStock: true,
rating: { $gte: 4.0 }
});
expect(results.length).toBeGreaterThan(0);
results.forEach(product => {
expect(product.category).toBe('Electronics');
expect(product.price).toBeLessThanOrEqual(1000);
expect(product.inStock).toBe(true);
expect(product.rating).toBeGreaterThanOrEqual(4.0);
});
});
});Frequently Asked Questions โ
How do I filter by multiple conditions with OR logic? โ
Use separate filter calls or predicates:
// Option 1: Separate filters and combine
const electronics = filter(products, { category: 'Electronics' });
const books = filter(products, { category: 'Books' });
const result = [...electronics, ...books];
// Option 2: Use $in operator
const result = filter(products, {
category: { $in: ['Electronics', 'Books'] }
});
// Option 3: Predicate function
const result = filter(products, (p) =>
p.category === 'Electronics' || p.category === 'Books'
);Can I filter nested arrays? โ
Use predicate functions:
const data = [
{ id: 1, items: [{ name: 'A' }, { name: 'B' }] },
{ id: 2, items: [{ name: 'C' }] }
];
const result = filter(data, (item) =>
item.items.some(subItem => subItem.name === 'A')
);How do I handle null/undefined values? โ
const data = [
{ id: 1, value: null },
{ id: 2, value: undefined },
{ id: 3, value: 'test' }
];
// Filter for null
filter(data, { value: null }); // Returns id: 1
// Filter out null/undefined
filter(data, (item) => item.value != null); // Returns id: 3What's the performance impact of operators vs predicates? โ
Operators are generally faster due to optimizations:
// โ
Faster: Operators with early exit
filter(data, { age: { $gte: 18 } });
// โ ๏ธ Slower: Predicate (more flexible but slower)
filter(data, (item) => item.age >= 18);
// For best performance: Use operators when possibleCan I use this in React/Vue/Angular? โ
Yes! Works in any JavaScript/TypeScript environment:
// React
const FilteredList = () => {
const [products, setProducts] = useState([/* ... */]);
const [priceFilter, setPriceFilter] = useState(100);
const filtered = filter(products, {
price: { $lte: priceFilter }
});
return <div>{filtered.map(p => <div key={p.id}>{p.name}</div>)}</div>;
};
// Vue
export default {
computed: {
filteredProducts() {
return filter(this.products, {
category: this.selectedCategory
});
}
}
};How do I debug complex filters? โ
// 1. Break down the filter
const step1 = filter(data, { category: 'Electronics' });
console.log('After category filter:', step1.length);
const step2 = filter(step1, { price: { $lte: 1000 } });
console.log('After price filter:', step2.length);
// 2. Test expression validity
import { validateExpression } from '@mcabreradev/filter';
validateExpression(myExpression);
// 3. Log results
const result = filter(data, expression);
console.log('Results:', result.length, result);Can operators be serialized to JSON? โ
Yes! This is a major advantage:
// Save filter to JSON
const filterExpr = {
price: { $gte: 100, $lte: 500 },
category: { $in: ['Electronics', 'Books'] }
};
localStorage.setItem('savedFilter', JSON.stringify(filterExpr));
// Load and use later
const loaded = JSON.parse(localStorage.getItem('savedFilter'));
const results = filter(products, loaded);How do I filter by date ranges? โ
const startDate = new Date('2025-01-01');
const endDate = new Date('2025-12-31');
const results = filter(orders, {
date: {
$gte: startDate,
$lte: endDate
}
});
// Last 30 days
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const recent = filter(orders, {
date: { $gte: thirtyDaysAgo }
});Can I chain multiple filter calls? โ
Yes:
const result = filter(
filter(
filter(data, { category: 'Electronics' }),
{ price: { $lte: 1000 } }
),
{ inStock: true }
);
// Or better: Combine in one call
const result = filter(data, {
category: 'Electronics',
price: { $lte: 1000 },
inStock: true
});How do I filter case-sensitive strings? โ
// Case-insensitive (default)
filter(users, 'alice'); // Matches 'Alice', 'ALICE', 'alice'
// Case-sensitive
filter(users, 'alice', { caseSensitive: true }); // Only 'alice'
// With operators
filter(users, {
name: { $startsWith: 'Al' }
}, { caseSensitive: true });Troubleshooting โ
"Invalid filter expression" error โ
Problem: Passing invalid expression types
// โ These cause errors
filter(data, undefined);
filter(data, Symbol('test'));
filter(data, new Map());Solution:
// โ
Valid expressions only
if (expression !== undefined && expression !== null) {
filter(data, expression);
}
// โ
Validate first
import { validateExpression } from '@mcabreradev/filter';
try {
validateExpression(expression);
filter(data, expression);
} catch (error) {
console.error('Invalid expression:', error.message);
}TypeScript type errors โ
Problem: Type mismatches
interface Product {
id: number;
name: string;
}
// โ Error: 'invalidProp' does not exist
filter<Product>(products, { invalidProp: 'value' });
// โ Error: Type 'string' not assignable
filter<Product>(products, { id: 'not-a-number' });Solution:
// โ
Use correct types
filter<Product>(products, { id: 1 });
filter<Product>(products, { name: 'test' });
// โ
Use type assertions carefully
filter(products, { someField: 'value' } as any);Unexpected results with wildcards โ
Problem: Wildcard not matching
// โ No results
filter(users, '%Alice%'); // Expecting matches but gets []Solution:
// Check case sensitivity
filter(users, '%Alice%', { caseSensitive: false }); // Default
// Check actual data
console.log('Sample data:', users[0]);
// Try simpler pattern
filter(users, 'Alice'); // Without wildcardsPerformance issues โ
Problem: Slow filtering on large datasets
// โ ๏ธ Slow: Complex predicate on 100k items
filter(largeDataset, (item) => {
// Complex calculations
return complexCondition(item);
});Solution:
// โ
Use operators
filter(largeDataset, {
price: { $gte: 100 },
category: { $in: ['A', 'B'] }
});
// โ
Enable cache for repeated queries
filter(largeDataset, expression, { enableCache: true });
// โ
Pre-filter with simple conditions
const preFiltered = filter(largeDataset, { inStock: true });
const final = filter(preFiltered, complexExpression);Cache not working โ
Problem: Cache doesn't seem to improve performance
Checklist:
- โ
Enable cache:
{ enableCache: true } - โ Use identical expressions (objects must be same reference or identical)
- โ Data hasn't changed between calls
- โ Filtering same dataset
Operators not detected โ
Problem: Operators treated as regular values
// โ Might be treated as literal object
const expr = { price: { $gt: 100 } };
filter(products, expr); // Not workingSolution:
// โ
Ensure correct syntax
filter(products, {
price: { $gt: 100 } // Inline or properly structured
});
// โ
Check TypeScript types
import type { ComparisonOperators } from '@mcabreradev/filter';
const priceFilter: ComparisonOperators = { $gt: 100 };
filter(products, { price: priceFilter });Contributing & Support โ
Contributing โ
We welcome contributions! Please see the contributing guidelines for details.
How to Contribute:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Submit a pull request
Areas for Contribution:
- Bug fixes
- Documentation improvements
- Performance optimizations
- New operator ideas
- Example use cases
Reporting Bugs โ
Please open an issue on GitHub with:
- Clear description of the bug
- Minimal reproduction example
- Expected vs actual behavior
- Environment details (Node.js version, TypeScript version)
Feature Requests โ
We love hearing your ideas! Open an issue with:
- Use case description
- Proposed API (if applicable)
- Why existing features don't solve it
Support โ
- GitHub Issues: Bug reports and feature requests
- GitHub Discussions: Questions and community support
- Documentation: This wiki and README
- Examples: Check
examples/directory
Version Support Policy โ
- v3.x: No longer supported
Version History โ
v5.2.4 (Latest - October 2025) โ
Major Features:
- ๐ค Intelligent Autocomplete - Type-aware operator suggestions
- ๐๏ธ Nested Object Autocomplete - 4-level deep autocomplete support
- ๐ Regex Operators - $regex and $match for pattern matching
- ๐ Logical Operators - $and, $or, $not for complex queries
- ๐จ Lazy Evaluation - filterLazy, filterFirst for efficient processing
- ๐ Enhanced Caching - Multi-layer memoization (530x faster)
- ๐ฆ 18 Total Operators - Complete MongoDB-style operator suite
Operator Count:
- Comparison: 6 operators ($gt, $gte, $lt, $lte, $eq, $ne)
- Array: 4 operators ($in, $nin, $contains, $size)
- String: 5 operators ($startsWith, $endsWith, $contains, $regex, $match)
- Logical: 3 operators ($and, $or, $not)
- Total: 18 operators
Performance Improvements:
- 530x faster with result caching
- 500x faster with lazy evaluation for early exits
- 80x less memory usage with filterLazy
- Optimized regex pattern caching
Breaking Changes:
- None (100% backward compatible)
Improvements:
- Better TypeScript inference for nested objects
- Improved performance with lazy evaluation
- Enhanced documentation with all features
- Real-world examples for all operators
- 463+ comprehensive tests