String distance & similarity for JavaScript
Levenshtein, Damerau-Levenshtein, Hamming, Jaro-Winkler — plus the Kyori ranking algorithm and the ready-made KyoriIndex for token-sensitive autocomplete scoring. Every metric is its own package subpath, so you can require methods/levensthein on its own and leave the rest behind. Every metric implemented natively — pure JavaScript, MIT-licensed.
In Japanese, 距離 (きょり) means "distance".
npm install @wizhut_tech/kyori Every method exposes the same three functions — distance(), similarity(), and rank() — so you can swap metrics without touching your call sites. Everything arrives in one package under two namespaces — methods for the metrics, indices for the search index — so reach for search() when you want a whole vocabulary ranked.
Lower is more similar — zero means identical.
const { levensthein } = require( '@wizhut_tech/kyori/methods/levensthein') const { damerau_levensthein } = require( '@wizhut_tech/kyori/methods/damerau_levensthein') const { hamming } = require( '@wizhut_tech/kyori/methods/hamming') // …/methods/<name> — one metric, nothing else levensthein.distance('foo', 'food') // → 1 levensthein.similarity('foo', 'food') // → 0.75 levensthein.rank('foo', ['food', 'foo']) // → [ { term: 'foo', score: 0 }, // { term: 'food', score: 1 } ] // a transposition costs one edit, not two damerau_levensthein.distance('form', 'from') // → 1 // equal length only — -1 rather than a guess hamming.distance('karolin', 'kathrin') // → 3 hamming.distance('foo', 'food') // → -1
Jaro-Winkler scores up to 1. The Kyori ranking cost lives on distance() and counts down to 0 — lower is better.
const { jaro_winkler } = require('@wizhut_tech/kyori/methods/jaro_winkler') const { kyori } = require('@wizhut_tech/kyori/methods/kyori') jaro_winkler.similarity('Maria', 'Marie') // → 0.92 // distance() is the ranking cost — lower is better // it folds case, accents and hyphens first kyori.distance('foo-bar', 'foo bar') // → 0 kyori.distance('cafe', 'café') // → 0 // a typed prefix ignores what you haven't typed kyori.distance('foo', 'food') // → 0 // one mistyped character costs one, not the rest kyori.distance('fod', 'food') // → 1 // same keywords in a different order: a flat 1 kyori.distance('hotel bel-air', 'bel-air hotel') // → 1 // rank(): lowest cost first, and a tie goes to the shorter kyori.rank('pa', ['paper towels', 'paint', 'pan']) // → pan, paint, paper towels — all at cost 0 // similarity() is token overlap — higher is better kyori.similarity('hotel bel-air', 'bel-air hotel') // → 1
Load your vocabulary once, and search() returns every term scored and sorted — best match first, the shorter term first among equals.
const { KyoriIndex } = require('@wizhut_tech/kyori/indices/kyori') const index = new KyoriIndex() index.addMany(['cart', 'artist']) index.addOne('art') index.count() // → 3 index.search('art') // → [ { term: 'art', score: 0 }, // { term: 'artist', score: 0 }, // { term: 'cart', score: 5 } ]
What's in the box
Five well-known algorithms and a ready-made search index — picked for the work most JavaScript apps actually need to do.
Classic edit distance — counts the minimum single-character insertions, deletions, and substitutions to turn one string into another.
Levenshtein plus transpositions, so "form" → "from" costs 1 instead of 2. Better for typo-tolerant matching.
Character-by-character difference for equal-length strings. Cheap and useful when alignment is fixed (codes, fingerprints, hashes); returns -1 when the lengths differ, rather than guessing.
Tuned for short strings like names — favours matching prefixes, which makes it the right pick for "did you mean…" on people and places.
The reason this library exists: a token-sensitive score built for autocomplete. Matching folds case, Latin accents, and hyphens, prefers hits at the start of a word, and charges a flat 1 for the same words in a different order. A typed prefix costs only the typos between it and the start of a suggestion — none tolerated until the third character — and among equally good matches rank() puts the shorter completion first. Use distance() or rank() to order results — lower is better, 0 is a perfect match — and similarity() for plain token overlap.
Autocomplete without a search engine: load your terms once with addOne() or addMany(), call search(), and get every term back scored and sorted — best match first, and among equal matches the shorter term first. Built on kyori.rank(), ships in the same package, pure JavaScript like everything else.
Open source
Free, MIT-licensed, no telemetry, no heavyweight dependencies — every metric implemented in-repo. Works in Node, the browser, and edge runtimes.