String similarity helpers powered by Dice's coefficient. Use it to rank fuzzy matches, measure duplicate content, or power smart suggestions with predictable, deterministic scoring.
- Modern TypeScript codebase with typed ESM builds
- Fast O(n) bigram comparison implementation
- Zero dependencies and side-effect free for optimal tree shaking
- Works anywhere a JavaScript runtime with ES2020 support is available
# pick the package manager you prefer
bun add @nivalis/string-similarity
# or
npm install @nivalis/string-similarity
# or
pnpm add @nivalis/string-similarityThe package is ESM-only. Use Node.js 18+, Bun, Deno, or a bundler that understands ESM.
import { compareTwoStrings, findBestMatch } from '@nivalis/string-similarity';
const similarity = compareTwoStrings('healed', 'sealed');
// similarity === 0.8
const { ratings, bestMatch } = findBestMatch('healed', [
'mailed',
'sealed',
'theatre',
]);
/* ratings === [
{ target: 'mailed', rating: 0.4 },
{ target: 'sealed', rating: 0.8 },
{ target: 'theatre', rating: 0.36363636363636365 },
] */
/* bestMatch === { target: 'sealed', rating: 0.8 } */Returns a score between 0 and 1. Whitespace is stripped before comparison and the order of arguments does not matter. When both inputs are empty or whitespace-only, the score is 1 only if the raw strings are strictly equal, and 0 otherwise (e.g. ' ' vs '\t' scores 0).
first/second: Strings with at least two characters for the best signal- Returns:
numbersimilarity score
compareTwoStrings('french', 'quebec');
// 0
compareTwoStrings('Olive-green table for sale, in extremely good condition.',
'For sale: table in very good condition, olive green in colour.');
// 0.6060606060606061Evaluates every entry in targetStrings and returns:
ratings: ordered array of{ target: string, rating: number }bestMatch: the record with the highest ratingbestMatchIndex: the index ofbestMatchinsidetargetStrings
const result = findBestMatch('Olive-green table for sale, in extremely good condition.', [
'For sale: green Subaru Impreza, 210,000 miles',
'For sale: table in very good condition, olive green in colour.',
'Wanted: mountain bike with at least 21 gears.',
]);
result.bestMatch.target;
// 'For sale: table in very good condition, olive green in colour.'Invalid arguments throw an error. Pass a non-empty mainString and a non-empty array of strings.
- Based on bigram overlap (Dice coefficient) for predictable rankings
- Ignores whitespace and repeated bigrams to reduce noise
- Inputs are Unicode-normalized to NFC before comparison, so composed and decomposed forms of the same text (e.g.
'café'in NFC vs NFD) score identically - Known limitation: bigrams are formed from UTF-16 code units, so surrogate pairs (emoji and other astral-plane characters) are split across bigrams. Results stay deterministic and symmetric, but scores for emoji-heavy strings are approximate
- Complexity is O(n) relative to total input length, making it suitable for realtime UI filtering
bun install # install dependencies
bun test # run the Bun test suite
bun run lint # biome static analysis
bun run build # compile to dist/ via tsdownAutomated hooks are managed by Lefthook. See CONTRIBUTING.md for detailed workflows, branch strategy, and release guidance.
- Export an async/bulk API that can precompute bigrams for a target list and reuse them across multiple queries, reducing redundant work for search or autocomplete scenarios.
- Converted the library to TypeScript and ESM-only exports
- Switched to named exports
compareTwoStringsandfindBestMatch - Removed UMD/browser bundles in favor of modern bundler workflows
MIT © Nivalis Studio