Kyori

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
Library · JavaScript · MIT

Five algorithms, one consistent shape.

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.

Edit-distance metrics

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

Similarity & ranking

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

Use case — autocomplete with KyoriIndex

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 algorithms and an index, picked for real work

Five well-known algorithms and a ready-made search index — picked for the work most JavaScript apps actually need to do.

01

Levenshtein

Classic edit distance — counts the minimum single-character insertions, deletions, and substitutions to turn one string into another.

02

Damerau-Levenshtein

Levenshtein plus transpositions, so "form" → "from" costs 1 instead of 2. Better for typo-tolerant matching.

03

Hamming

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.

04

Jaro-Winkler

Tuned for short strings like names — favours matching prefixes, which makes it the right pick for "did you mean…" on people and places.

05

Kyori ranking

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.

06

KyoriIndex

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

Ready to drop in

Free, MIT-licensed, no telemetry, no heavyweight dependencies — every metric implemented in-repo. Works in Node, the browser, and edge runtimes.