Flexible string matching utilities for filtering data based on user input. The matching system supports simple string arrays, object properties, nested properties, and custom matching functions with case-insensitive, normalized comparisons.
Filter a list of items based on case-insensitive string matching.
/**
* Filter items based on case-insensitive string matching
* @param input - Text to match against list items
* @param list - Array of items to filter (strings or objects)
* @param target - Property name, nested path, or custom matching function
* @returns Filtered array maintaining original types
*/
function matches<T>(input: string, list: T[], target?: string | MatchFunction<T>): T[];
type MatchFunction<T> = (item: T, input: string) => boolean;Basic String Matching:
import alfy from "alfy";
// Simple string array matching
const fruits = ['apple', 'banana', 'orange', 'pineapple'];
const matches = alfy.matches('app', fruits);
console.log(matches); // ['apple', 'pineapple']
// Case-insensitive matching
const colors = ['Red', 'GREEN', 'blue', 'Yellow'];
const redMatches = alfy.matches('red', colors);
console.log(redMatches); // ['Red']Object Property Matching:
import alfy from "alfy";
const users = [
{ name: 'Alice Johnson', role: 'admin' },
{ name: 'Bob Smith', role: 'user' },
{ name: 'Charlie Brown', role: 'admin' }
];
// Match against 'name' property
const nameMatches = alfy.matches('alice', users, 'name');
console.log(nameMatches); // [{ name: 'Alice Johnson', role: 'admin' }]
// Match against 'role' property
const adminMatches = alfy.matches('admin', users, 'role');
console.log(adminMatches); // Both Alice and CharlieNested Property Matching:
import alfy from "alfy";
const employees = [
{
personal: { firstName: 'John', lastName: 'Doe' },
position: { title: 'Developer', department: 'Engineering' }
},
{
personal: { firstName: 'Jane', lastName: 'Smith' },
position: { title: 'Designer', department: 'Creative' }
}
];
// Match against nested properties using dot notation
const firstNameMatches = alfy.matches('john', employees, 'personal.firstName');
const departmentMatches = alfy.matches('eng', employees, 'position.department');Custom Matching Functions:
import alfy from "alfy";
const products = [
{ name: 'MacBook Pro', price: 2399, category: 'laptop' },
{ name: 'iPhone 15', price: 999, category: 'phone' },
{ name: 'iPad Air', price: 699, category: 'tablet' }
];
// Custom function for multiple field matching
const multiFieldMatches = alfy.matches('pro', products, (item, input) => {
const searchIn = `${item.name} ${item.category}`.toLowerCase();
return searchIn.includes(input.toLowerCase());
});
// Custom function for price range matching
const expensiveMatches = alfy.matches('expensive', products, (item, input) => {
if (input.toLowerCase() === 'expensive') {
return item.price > 1000;
}
return item.name.toLowerCase().includes(input.toLowerCase());
});
// Exact matching function
const exactMatches = alfy.matches('laptop', products, (item, input) => {
return item.category === input.toLowerCase();
});Same as matches() but automatically uses alfy.input as the search term.
/**
* Filter items using alfy.input as the search term
* @param list - Array of items to filter
* @param target - Property name, nested path, or custom matching function
* @returns Filtered array maintaining original types
*/
function inputMatches<T>(list: T[], target?: string | MatchFunction<T>): T[];Usage Examples:
import alfy from "alfy";
// Assume user types "john" in Alfred
const users = [
{ name: 'John Doe', email: 'john@example.com' },
{ name: 'Jane Smith', email: 'jane@example.com' },
{ name: 'Johnny Cash', email: 'johnny@example.com' }
];
// Filter by name using user input
const nameMatches = alfy.inputMatches(users, 'name');
// Returns John Doe and Johnny Cash
// Filter by email using user input
const emailMatches = alfy.inputMatches(users, 'email');
// Returns John Doe and Johnny Cash (matches john@ in email)
// Multiple field search
const multiFieldMatches = alfy.inputMatches(users, (item, input) => {
const searchText = `${item.name} ${item.email}`.toLowerCase();
return searchText.includes(input.toLowerCase());
});API Response Filtering:
import alfy from "alfy";
// Fetch data and filter based on user input
const posts = await alfy.fetch('https://jsonplaceholder.typicode.com/posts');
const matchingPosts = alfy.inputMatches(posts, (post, input) => {
// Search in both title and body
const searchIn = `${post.title} ${post.body}`.toLowerCase();
return searchIn.includes(input.toLowerCase());
});
const items = matchingPosts.map(post => ({
title: post.title,
subtitle: post.body.substring(0, 100) + '...',
arg: post.id
}));
alfy.output(items);File Search with Multiple Criteria:
import alfy from "alfy";
const files = [
{ name: 'report.pdf', size: 1024, type: 'document' },
{ name: 'image.jpg', size: 2048, type: 'image' },
{ name: 'backup.zip', size: 5120, type: 'archive' }
];
// Smart search supporting name, type, or size descriptions
const smartMatches = alfy.inputMatches(files, (file, input) => {
const input_lower = input.toLowerCase();
// Match file name
if (file.name.toLowerCase().includes(input_lower)) {
return true;
}
// Match file type
if (file.type.includes(input_lower)) {
return true;
}
// Match size descriptions
if (input_lower === 'large' && file.size > 3000) {
return true;
}
if (input_lower === 'small' && file.size < 1500) {
return true;
}
return false;
});
const items = smartMatches.map(file => ({
title: file.name,
subtitle: `${file.type} • ${file.size} bytes`,
arg: file.name
}));
alfy.output(items);All matching operations use Unicode normalization and case-insensitive comparison:
import alfy from "alfy";
const items = ['café', 'naïve', 'résumé'];
// These all match despite different Unicode representations
const matches1 = alfy.matches('cafe', items); // Matches 'café'
const matches2 = alfy.matches('naive', items); // Matches 'naïve'
const matches3 = alfy.matches('resume', items); // Matches 'résumé'The matching system automatically handles: