import * as plugins from './smartfuzzy.plugins.js'; /** * Result of a fuzzy search on objects * * @typeParam T - The type of object being searched */ export interface IFuzzySearchResult { /** The matched object */ item: T; /** The index of the object in the original array */ refIndex: number; /** The match score (lower is better) */ score?: number; } /** * Handles fuzzy searching and sorting of objects based on their properties * * @typeParam T - The type of objects to search through * * @example * ```typescript * interface User { * name: string; * email: string; * } * * const users = [ * { name: 'John Smith', email: 'john@example.com' }, * { name: 'Jane Doe', email: 'jane@example.com' } * ]; * * const sorter = new ObjectSorter(users); * const results = sorter.sort('john', ['name', 'email']); * ``` */ export class ObjectSorter { /** * The collection of objects to search through */ public objectDictionary: T[]; /** * Creates a new ObjectSorter instance * * @param objectDictionaryArg - Array of objects to search through */ constructor(objectDictionaryArg: T[] = []) { if (objectDictionaryArg !== undefined && !Array.isArray(objectDictionaryArg)) { throw new Error('Object dictionary must be an array'); } this.objectDictionary = objectDictionaryArg; } /** * Searches and sorts objects based on how well they match the search string * in the specified object properties * * @param stringArg - The search query string * @param objectKeysArg - Array of object property names to search within * @returns Array of results sorted by relevance (best matches first) * * @example * ```typescript * // Search for 'john' in both name and email fields * const results = sorter.sort('john', ['name', 'email']); * * // First result is the best match * console.log(results[0].item.name); // 'John Smith' * ``` */ public sort(stringArg: string, objectKeysArg: string[]): Array> { if (typeof stringArg !== 'string') { throw new Error('Search string must be a string'); } if (!Array.isArray(objectKeysArg)) { throw new Error('Object keys must be an array'); } if (objectKeysArg.length === 0) { throw new Error('At least one object key must be provided for searching'); } // Verify all keys are strings for (const key of objectKeysArg) { if (typeof key !== 'string') { throw new Error('All object keys must be strings'); } } // Empty dictionary should return empty results instead of error if (this.objectDictionary.length === 0) { return []; } const fuseOptions = { shouldSort: true, threshold: 0.6, // Lower values = more strict matching location: 0, // Where to start searching in the string distance: 100, // How far to search in the string maxPatternLength: 32, minMatchCharLength: 1, keys: objectKeysArg, }; const fuse = new plugins.fuseJs(this.objectDictionary, fuseOptions); const result = fuse.search(stringArg); return result; } }