Skip to content

A powerful, zero-dependency filtering library that brings MongoDB-style operators, SQL wildcards, and intelligent autocomplete to TypeScript arrays. Think of it as Array.filter() on steroids! ๐Ÿ’ช

License

Notifications You must be signed in to change notification settings

mcabreradev/filter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

@mcabreradev/filter

Filter arrays like a pro. A powerful, SQL-like array filtering library for TypeScript with advanced pattern matching, MongoDB-style operators, deep object comparison, geospatial queries, and zero dependencies.

Quick Start โ€ข Why You'll Love It โ€ข Examples โ€ข Playground โ€ข Documentation


Table of Contents


The Hook

Tired of writing complex filter logic? Stop wrestling with nested Array.filter() chains and verbose conditionals. Write clean, declarative filters that read like queries.

Before:

const results = data.filter(item =>
  item.age >= 18 &&
  item.status === 'active' &&
  (item.role === 'admin' || item.role === 'moderator') &&
  item.email.endsWith('@company.com') &&
  item.createdAt >= thirtyDaysAgo
);

After:

const results = filter(data, {
  age: { $gte: 18 },
  status: 'active',
  role: ['admin', 'moderator'],
  email: { $endsWith: '@company.com' },
  createdAt: { $gte: thirtyDaysAgo }
});

Same result. 70% less code. 100% more readable.


Quick Start

Install

npm install @mcabreradev/filter
# or
pnpm add @mcabreradev/filter
# or
yarn add @mcabreradev/filter

Requirements: Node.js >= 20, TypeScript 5.0+ (optional)

Your First Filter

import { filter } from '@mcabreradev/filter';

const users = [
  { name: 'Alice', age: 30, city: 'Berlin', active: true },
  { name: 'Bob', age: 25, city: 'London', active: false },
  { name: 'Charlie', age: 35, city: 'Berlin', active: true }
];

// Simple string search
const berlinUsers = filter(users, 'Berlin');
// โ†’ [{ name: 'Alice', ... }, { name: 'Charlie', ... }]

// Object-based filtering
const activeBerlinUsers = filter(users, {
  city: 'Berlin',
  active: true
});
// โ†’ [{ name: 'Alice', ... }]

// MongoDB-style operators
const adults = filter(users, {
  age: { $gte: 18 }
});
// โ†’ All users (all are 18+)

// That's it! You're filtering like a pro.

๐ŸŽฎ Try it in the Playground โ†’


Why You'll Love It

๐Ÿš€ Blazing Fast

  • 530x faster with optional caching
  • 500x faster with lazy evaluation for large datasets
  • Optimized for production workloads

๐ŸŽฏ Developer Friendly

  • Intuitive API that feels natural
  • SQL-like syntax you already know
  • Full TypeScript support with intelligent autocomplete

๐Ÿ”ง Incredibly Flexible

  • Multiple filtering strategies (strings, objects, operators, predicates)
  • Works with any data structure
  • Combine approaches seamlessly

๐Ÿ“ฆ Production Ready

  • 993+ tests ensuring reliability
  • Zero dependencies (12KB gzipped)
  • Used in production by companies worldwide
  • MIT licensed

๐Ÿชถ Ultra Lightweight

  • Truly zero dependencies!
  • Tiny 12KB bundle
  • Optional Zod for validation
  • No bloat, just pure filtering power

๐Ÿ”’ Type-Safe by Default

  • Built with strict TypeScript
  • Catch errors at compile time
  • Full IntelliSense and autocomplete support

๐ŸŽจ Framework Agnostic

  • Works everywhere: React, Vue, Svelte, Angular, SolidJS, Preact
  • First-class hooks and composables included
  • SSR compatible (Next.js, Nuxt, SvelteKit)

๐Ÿ“Š Handles Big Data

  • Process millions of records efficiently
  • Lazy evaluation for memory optimization
  • Built for scale

Examples

Basic Filtering

// String matching - searches all properties
filter(products, 'Laptop');

// Object matching - AND logic
filter(products, {
  category: 'Electronics',
  price: { $lt: 1000 }
});

// Wildcard patterns (SQL-like)
filter(users, '%alice%');  // Contains 'alice'
filter(users, 'Al%');      // Starts with 'Al'
filter(users, '%son');     // Ends with 'son'

MongoDB-Style Operators

// Comparison operators
filter(products, {
  price: { $gte: 100, $lte: 500 }
});

// Array operators
filter(products, {
  category: { $in: ['Electronics', 'Books'] },
  tags: { $contains: 'sale' }
});

// String operators
filter(users, {
  email: { $endsWith: '@company.com' },
  name: { $startsWith: 'John' }
});

// Logical operators
filter(products, {
  $and: [
    { inStock: true },
    {
      $or: [
        { rating: { $gte: 4.5 } },
        { price: { $lt: 50 } }
      ]
    }
  ]
});

Array OR Syntax (Intuitive!)

// Clean array syntax - no $in needed!
filter(products, {
  category: ['Electronics', 'Books']
});
// Equivalent to: { category: { $in: ['Electronics', 'Books'] } }

// Multiple properties
filter(users, {
  city: ['Berlin', 'Paris'],
  role: ['admin', 'moderator']
});

Geospatial Queries

import { filter, type GeoPoint } from '@mcabreradev/filter';

const userLocation: GeoPoint = { lat: 52.52, lng: 13.405 };

// Find restaurants within 5km
filter(restaurants, {
  location: {
    $near: {
      center: userLocation,
      maxDistanceMeters: 5000
    }
  },
  rating: { $gte: 4.5 }
});

Datetime Filtering

// Events in next 7 days
filter(events, {
  date: { $upcoming: { days: 7 } }
});

// Recent events (last 24 hours)
filter(events, {
  date: { $recent: { hours: 24 } }
});

// Weekday events during business hours
filter(events, {
  date: { $dayOfWeek: [1, 2, 3, 4, 5] },
  startTime: { $timeOfDay: { start: 9, end: 17 } }
});

// Users who logged in recently (last 7 days)
filter(users, {
  lastLogin: { $recent: { days: 7 } }
});

// Upcoming meetings in next 2 hours
filter(meetings, {
  startTime: { $upcoming: { hours: 2 } }
});

// Weekend events only
filter(events, {
  date: { $isWeekend: true }
});

// Calculate age (users over 18)
filter(users, {
  birthDate: { $age: { $gte: 18 } }
});

// Events before a specific date
filter(events, {
  date: { $isBefore: new Date('2025-12-31') }
});

Performance Optimization

// Enable caching for repeated queries
const results = filter(largeDataset, expression, {
  enableCache: true,
  orderBy: { field: 'price', direction: 'desc' },
  limit: 100
});

// Lazy evaluation for large datasets
import { filterFirst } from '@mcabreradev/filter';
const first10 = filterFirst(users, { premium: true }, 10);

Real-World: E-commerce Search

interface Product {
  id: number;
  name: string;
  price: number;
  category: string;
  brand: string;
  rating: number;
  inStock: boolean;
  tags: string[];
}

const products: Product[] = [...];

// Find affordable, highly-rated electronics in stock
const affordableElectronics = filter(products, {
  category: 'Electronics',
  price: { $lte: 1000 },
  rating: { $gte: 4.5 },
  inStock: true
});

// Search with multiple filters
const searchResults = filter(products, {
  name: { $contains: 'laptop' },
  brand: { $in: ['Apple', 'Dell', 'HP'] },
  price: { $gte: 500, $lte: 2000 }
});

// Sort results
const sortedProducts = filter(products, {
  category: 'Electronics',
  inStock: true
}, {
  orderBy: [
    { field: 'price', direction: 'asc' },
    { field: 'rating', direction: 'desc' }
  ],
  limit: 20
});

Framework Integrations

Works seamlessly with your favorite framework:

React

import { useFilter } from '@mcabreradev/filter/react';

function UserList() {
  const { filtered, isFiltering } = useFilter(users, { active: true });
  return <div>{filtered.map(u => <User key={u.id} {...u} />)}</div>;
}

Vue

<script setup>
import { useFilter } from '@mcabreradev/filter/vue';
const { filtered } = useFilter(users, { active: true });
</script>

Svelte

<script>
import { useFilter } from '@mcabreradev/filter/svelte';
const { filtered } = useFilter(users, writable({ active: true }));
</script>

Angular

import { FilterService } from '@mcabreradev/filter/angular';

@Component({
  providers: [FilterService],
  template: `
    @for (user of filterService.filtered(); track user.id) {
      <div>{{ user.name }}</div>
    }
  `
})
export class UserListComponent {
  filterService = inject(FilterService<User>);
}

SolidJS

import { useFilter } from '@mcabreradev/filter/solidjs';

function UserList() {
  const { filtered } = useFilter(
    () => users,
    () => ({ active: true })
  );
  return <For each={filtered()}>{(u) => <div>{u.name}</div>}</For>;
}

Preact

import { useFilter } from '@mcabreradev/filter/preact';

function UserList() {
  const { filtered } = useFilter(users, { active: true });
  return <div>{filtered.map(u => <div key={u.id}>{u.name}</div>)}</div>;
}

Features:

  • โœ… Full TypeScript support with generics
  • โœ… Debounced search hooks/services
  • โœ… Pagination support
  • โœ… SSR compatible
  • โœ… 100% test coverage

๐Ÿ“– Complete Framework Guide โ†’


Core Features

Supported Operators

Comparison: $gt, $gte, $lt, $lte, $eq, $ne Array: $in, $nin, $contains, $size String: $startsWith, $endsWith, $contains, $regex, $match Logical: $and, $or, $not Geospatial: $near, $geoBox, $geoPolygon Datetime: $recent, $upcoming, $dayOfWeek, $timeOfDay, $age, $isWeekday, $isWeekend, $isBefore, $isAfter

TypeScript Support

Full type safety with intelligent autocomplete:

interface Product {
  name: string;
  price: number;
  tags: string[];
}

filter<Product>(products, {
  price: {  }, // Autocomplete: $gt, $gte, $lt, $lte, $eq, $ne
  name: {  },  // Autocomplete: $startsWith, $endsWith, $contains, $regex
  tags: {  }   // Autocomplete: $in, $nin, $contains, $size
});

Configuration Options

filter(data, expression, {
  caseSensitive: false,      // Case-sensitive string matching
  maxDepth: 3,                // Max depth for nested objects
  enableCache: true,          // Enable result caching (530x faster)
  orderBy: 'price',           // Sort results
  limit: 10,                  // Limit number of results
  debug: true                 // Visual debugging mode
});

Advanced Features

Lazy Evaluation

Efficiently process large datasets with lazy evaluation:

import { filterLazy, filterFirst, filterExists, filterCount } from '@mcabreradev/filter';

// Process items on-demand
const filtered = filterLazy(millionRecords, { active: true });
for (const item of filtered) {
  process(item);
  if (shouldStop) break; // Early exit
}

// Find first N matches
const first10 = filterFirst(users, { premium: true }, 10);

// Check existence without processing all items
const hasAdmin = filterExists(users, { role: 'admin' });

// Count matches
const activeCount = filterCount(users, { active: true });

Benefits:

  • ๐Ÿš€ 500x faster for operations that don't need all results
  • ๐Ÿ’พ 100,000x less memory for large datasets
  • โšก Early exit optimization

๐Ÿ“– Lazy Evaluation Guide โ†’

Memoization & Caching

530x faster with optional caching:

// First call - processes data
const results = filter(largeDataset, { age: { $gte: 18 } }, { enableCache: true });

// Second call - returns cached result instantly
const sameResults = filter(largeDataset, { age: { $gte: 18 } }, { enableCache: true });

Performance Gains:

Scenario Without Cache With Cache Speedup
Simple query (10K items) 5.3ms 0.01ms 530x
Regex pattern 12.1ms 0.02ms 605x
Complex nested 15.2ms 0.01ms 1520x

๐Ÿ“– Memoization Guide โ†’

Visual Debugging

Built-in debug mode with expression tree visualization:

filter(users, { city: 'Berlin' }, { debug: true });

// Console output:
// โ”Œโ”€ Filter Debug Tree
// โ”‚  Expression: {"city":"Berlin"}
// โ”‚  Matched: 3/10 (30.0%)
// โ”‚  Execution time: 0.42ms
// โ””โ”€ โœ“ city = "Berlin"

๐Ÿ“– Debug Guide โ†’


Documentation

๐Ÿ“– Complete Guides

๐ŸŽฏ Quick Links


Performance

Filter is optimized for performance:

  • Operators use early exit strategies for fast evaluation
  • Regex patterns are compiled and cached
  • Optional caching for repeated queries (530x-1520x faster)
  • Lazy evaluation for efficient large dataset processing (500x faster)
  • Type guards for fast type checking
// โœ… Fast: Operators with early exit
filter(data, { age: { $gte: 18 } });

// โœ… Fast with caching for repeated queries
filter(largeData, expression, { enableCache: true });

// โœ… Fast with lazy evaluation for large datasets
const result = filterFirst(millionRecords, { active: true }, 100);

Bundle Size

Import Size (gzipped) Tree-Shakeable
Full 12 KB โœ…
Core only 8.4 KB โœ…
React hooks 9.2 KB โœ…
Lazy evaluation 5.4 KB โœ…

Browser Support

Works in all modern browsers and Node.js:

  • Node.js: >= 20
  • Browsers: Chrome, Firefox, Safari, Edge (latest versions)
  • TypeScript: >= 5.0
  • Module Systems: ESM, CommonJS

Migration from v3.x

Good news: v5.x is 100% backward compatible! All v3.x code continues to work.

// โœ… All v3.x syntax still works
filter(data, 'string');
filter(data, { prop: 'value' });
filter(data, (item) => true);
filter(data, '%pattern%');

// โœ… New in v5.x
filter(data, { age: { $gte: 18 } });
filter(data, expression, { enableCache: true });

๐Ÿ“– Migration Guide โ†’


Changelog

v5.8.3 (Current)

  • ๐Ÿ› Bug Fix: Fixed critical issue where limit option was ignored in cache key
  • โšก Performance: Replaced unbounded caches with LRU strategy to prevent memory leaks
  • ๐Ÿ”’ Stability: Improved memory management for long-running applications

v5.8.0

  • ๐ŸŽจ New Framework Integrations: Angular, SolidJS, and Preact support
  • ๐Ÿ”ข Limit Option: New limit configuration option to restrict result count
  • ๐Ÿ“Š OrderBy Option: New OrderBy configuration option to sort filtered results by field(s) in ascending or descending order
  • โœ… 993+ tests with comprehensive coverage

v5.7.0

  • ๐Ÿ…ฐ๏ธ Angular: Services and Pipes with Signals support
  • ๐Ÿ”ท SolidJS: Signal-based reactive hooks
  • โšก Preact: Lightweight hooks API

v5.6.0

  • ๐ŸŒ Geospatial Operators: Location-based filtering with $near, $geoBox, $geoPolygon
  • ๐Ÿ“… Datetime Operators: Temporal filtering with $recent, $upcoming, $dayOfWeek, $age

v5.5.0

  • ๐ŸŽจ Array OR Syntax: Intuitive array-based OR filtering
  • ๐Ÿ› Visual Debugging: Built-in debug mode with expression tree visualization
  • ๐ŸŽฎ Interactive Playground: Online playground for testing filters

๐Ÿ“– Full Changelog โ†’


Contributing

We welcome contributions! Please read our Contributing Guide for details.

Ways to Contribute:

  • Report bugs or request features via GitHub Issues
  • Submit pull requests with bug fixes or new features
  • Improve documentation
  • Share your use cases and examples

License

MIT License - see LICENSE.md for details.

Copyright (c) 2025 Miguelangel Cabrera


Support


Made with โค๏ธ for the JavaScript/TypeScript community

About

A powerful, zero-dependency filtering library that brings MongoDB-style operators, SQL wildcards, and intelligent autocomplete to TypeScript arrays. Think of it as Array.filter() on steroids! ๐Ÿ’ช

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Contributors 5