String distance & similarity for JavaScript
Levenshtein, Damerau-Levenshtein, Hamming, Jaro-Winkler — plus the Kyori ranking algorithm and the ready-made Kyori Index 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
Four metrics you already know, each with the case it is built for and one line of real output. Then the two pieces that are Kyori’s own.
The classic: the fewest single-character insertions, deletions and substitutions that turn one string into the other.
Best for spell-check suggestions, near-duplicate records, anything diff-shaped.
Levenshtein plus transpositions: two swapped neighbours count as one edit, not two — the shape most typos actually take.
Best for typo-tolerant matching on anything a person typed.
Position-by-position mismatches between two strings of equal length. No alignment step, so it is the cheapest of the five.
Best for fixed-width codes, hashes, fingerprints and bit strings.
Made for short strings like names: rewards characters that match near the same position, with a bonus for a shared prefix.
Best for "did you mean…" on people, places and product names.
The reason this library exists
A token-sensitive cost built for one job: ordering suggestions under a query someone is still typing.
Autocomplete without a search engine
Load a vocabulary once and search it on every keystroke. Every term comes back scored by kyori.rank() and sorted — best match first, shorter term first among equals.
Open source
Free, MIT-licensed, no telemetry, no heavyweight dependencies — every metric implemented in-repo. Works in Node, the browser, and edge runtimes.