Metadata-Version: 2.4
Name: matchr
Version: 0.1.3
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Rust
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Topic :: Text Processing
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Intended Audience :: Developers
License-File: LICENSE
Summary: Fast fuzzy string matching — Levenshtein, Jaro-Winkler, and trigram similarity. Written in Rust.
Keywords: fuzzy,string-matching,levenshtein,jaro-winkler,similarity
Home-Page: https://github.com/mommo-codes/matchr
License: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://github.com/mommo-codes/matchr
Project-URL: Repository, https://github.com/mommo-codes/matchr

# matchr

[![PyPI](https://img.shields.io/pypi/v/matchr)](https://pypi.org/project/matchr/)

Fast fuzzy string matching — written in Rust, usable from Python.

## Install

```bash
pip install matchr
```

## Python quick start

```python
from matchr import best_match, rank_matches, batch_best_match

# find the closest match
best_match("oatly oat drink", ["Oatly Oat Drink 1L", "Oat Milk", "Oatly Barista"])
# → ('Oatly Oat Drink 1L', 0.902)

# filter weak matches with a threshold
best_match("xyz gibberish", ["Oatly Oat Drink 1L"], threshold=0.7)
# → None

# match many queries at once
batch_best_match(["oatly oat drink", "felix cat food"], catalog, threshold=0.7)
# → [('Oatly Oat Drink 1L', 0.902), ('Felix Cat Food 400g', 0.843)]
```

## Rust usage

```rust
use matchr::{levenshtein, jaro_winkler, trigram_similarity};

fn main() {
    println!("{}", levenshtein("cat", "bat"));           // 1
    println!("{}", jaro_winkler("martha", "marhta"));    // 0.961
    println!("{}", trigram_similarity("hello", "helo")); // 0.4
}
```

## Algorithms

- **Levenshtein** — minimum edit distance between two strings. Lower = more similar.
- **Jaro-Winkler** — similarity score from `0.0` to `1.0`, optimised for names and short strings. Gives a bonus for shared prefixes.
- **Trigram** — splits strings into overlapping 3-character chunks, scores overlap using the Dice coefficient. Good for longer strings and typo detection.
- **Token sort / token set** — order-invariant scorers built on top of `combined_score`. Sorts (and optionally deduplicates) whitespace-separated tokens before comparing, so `"Oat Drink Oatly 1L"` and `"Oatly Oat Drink 1L"` come out equal.
- **Combined score** — weighted blend of Levenshtein, Jaro-Winkler, and trigram. Used internally by `best_match`, `rank_matches`, and `batch_best_match`.

## Benchmarks

See [`benchmarks/`](benchmarks/) for the criterion suite (`cargo bench`) and a Python comparison vs `rapidfuzz`. There's also a [demo notebook](benchmarks/demo.ipynb) that walks through deduping a synthetic 10k-product catalog end-to-end.

## Notes

- All functions normalise input (trim + lowercase + strip diacritics) before comparing — `"Café"` and `"cafe"` are treated as equal
- `levenshtein` returns `usize` (edit distance), all others return `f64` (0.0–1.0)
- Python functions accept an optional `threshold` parameter — results below it are filtered out
- `batch_best_match` is parallelised via rayon and pre-normalises the candidate list once, so it scales with available cores
