or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

alfred-integration.mdconfiguration-storage.mderror-handling.mdhttp-client.mdindex.mdinput-output.mdstring-matching.md
tile.json

string-matching.mddocs/

String Matching

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.

Capabilities

Matches Function

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 Charlie

Nested 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();
});

Input Matches Function

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());
});

Practical Workflow Examples

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);

String Normalization

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:

  • Case differences (uppercase/lowercase)
  • Unicode normalization (accented characters)
  • Whitespace normalization
  • String containment (partial matches)