smartfuzzy/ts/smartfuzzy.classes.smartfuzzy.ts

158 lines
4.4 KiB
TypeScript

import * as plugins from './smartfuzzy.plugins.js';
export let standardExport = 'Hi there! :) This is an exported string';
/**
* Type representing a dictionary of words mapped to their scores
* Lower scores typically indicate better matches
*/
export type TDictionaryMap = { [key: string]: number };
/**
* Main class for fuzzy string matching against a dictionary
*
* @example
* ```typescript
* const fuzzy = new Smartfuzzy(['apple', 'banana', 'orange']);
* const result = fuzzy.findClosestMatch('aple'); // Returns 'apple'
* ```
*/
export class Smartfuzzy {
/**
* Array of words used for fuzzy matching
*/
public dictionary: string[];
/**
* Creates a new Smartfuzzy instance
*
* @param dictionary - Initial array of words to use for matching
*/
constructor(dictionary: string[]) {
if (!Array.isArray(dictionary)) {
throw new Error('Dictionary must be an array of strings');
}
this.dictionary = dictionary;
}
/**
* Adds one or more words to the dictionary
*
* @param payloadArg - A single word or an array of words to add to the dictionary
* @returns void
*
* @example
* ```typescript
* fuzzy.addToDictionary('pear');
* fuzzy.addToDictionary(['pear', 'grape', 'kiwi']);
* ```
*/
public addToDictionary(payloadArg: string | string[]): void {
if (payloadArg === undefined || payloadArg === null) {
throw new Error('Input cannot be null or undefined');
}
if (Array.isArray(payloadArg)) {
// Validate all items in array are strings
for (const item of payloadArg) {
if (typeof item !== 'string') {
throw new Error('All items in array must be strings');
}
}
this.dictionary = this.dictionary.concat(payloadArg);
} else if (typeof payloadArg === 'string') {
this.dictionary.push(payloadArg);
} else {
throw new Error('Input must be a string or an array of strings');
}
}
/**
* Calculates the Levenshtein distance (edit distance) between the input string
* and each word in the dictionary
*
* @param stringArg - The string to compare against the dictionary
* @returns A dictionary map where keys are words and values are edit distances
*
* @example
* ```typescript
* const scores = fuzzy.calculateScores('aple');
* // Returns: { 'apple': 1, 'banana': 5, 'orange': 5 }
* ```
*/
public calculateScores(stringArg: string): TDictionaryMap {
if (typeof stringArg !== 'string') {
throw new Error('Input must be a string');
}
if (this.dictionary.length === 0) {
throw new Error('Dictionary is empty');
}
const dictionaryMap: TDictionaryMap = {};
for (const wordArg of this.dictionary) {
dictionaryMap[wordArg] = plugins.leven(stringArg, wordArg);
}
return dictionaryMap;
}
/**
* @deprecated Use calculateScores instead
*/
public getChangeScoreForString(stringArg: string): TDictionaryMap {
return this.calculateScores(stringArg);
}
/**
* Finds the closest matching word in the dictionary using fuzzy search
*
* @param stringArg - The string to find a match for
* @returns The closest matching word, or null if no match is found or dictionary is empty
*
* @example
* ```typescript
* const match = fuzzy.findClosestMatch('oragne');
* // Returns: 'orange'
* ```
*/
public findClosestMatch(stringArg: string): string {
if (typeof stringArg !== 'string') {
throw new Error('Input must be a string');
}
if (this.dictionary.length === 0) {
return null; // Return null for empty dictionary instead of throwing error
}
const fuseDictionary: { name: string }[] = [];
for (const wordArg of this.dictionary) {
fuseDictionary.push({
name: wordArg,
});
}
const fuseOptions = {
shouldSort: true,
threshold: 0.6,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: ['name'],
};
const fuse = new plugins.fuseJs(fuseDictionary, fuseOptions);
const fuzzyResult = fuse.search(stringArg);
let closestMatch: string = null;
if (fuzzyResult.length > 0) {
closestMatch = fuzzyResult[0].item.name;
}
return closestMatch;
}
/**
* @deprecated Use findClosestMatch instead
*/
public getClosestMatchForString(stringArg: string): string {
return this.findClosestMatch(stringArg);
}
}