or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-match-sorter

Simple, expected, and deterministic best-match sorting of an array in JavaScript

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/match-sorter@8.1.x

To install, run

npx @tessl/cli install tessl/npm-match-sorter@8.1.0

index.mddocs/

match-sorter

match-sorter provides simple, expected, and deterministic best-match sorting of arrays in JavaScript. It implements an intelligent ranking algorithm that sorts items based on multiple match criteria including exact matches, prefix matches, substring matches, acronym matches, and fuzzy matching.

Package Information

  • Package Name: match-sorter
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install match-sorter

Core Imports

// Main function and utilities
import { matchSorter, rankings, defaultBaseSortFn } from "match-sorter";

// Import with types (TypeScript)
import { 
  matchSorter, 
  rankings, 
  defaultBaseSortFn,
  type MatchSorterOptions,
  type KeyAttributesOptions,
  type KeyOption,
  type KeyAttributes,
  type RankingInfo,
  type ValueGetterKey
} from "match-sorter";

For CommonJS:

const { matchSorter, rankings, defaultBaseSortFn } = require("match-sorter");

Basic Usage

import { matchSorter } from "match-sorter";

// Simple string array matching
const items = ["apple", "banana", "grape", "orange"];
const results = matchSorter(items, "ap");
// Returns: ["apple"] (starts with "ap")

// Object matching with keys
const users = [
  { name: "Sarah Connor", email: "sarah@example.com" },
  { name: "John Connor", email: "john@example.com" },
  { name: "Kyle Reese", email: "kyle@example.com" }
];

const matches = matchSorter(users, "connor", { keys: ["name"] });
// Returns: [{ name: "Sarah Connor", ... }, { name: "John Connor", ... }]

Architecture

match-sorter uses a hierarchical ranking system that evaluates matches based on quality:

  • Rankings System: Seven match quality levels from exact matches (highest) to fuzzy matches (lowest)
  • Key-based Matching: Support for searching object properties, including nested properties
  • Configurable Thresholds: Minimum match quality filtering with per-key customization
  • Accent Handling: Optional diacritic removal for international text matching
  • Custom Sorting: Pluggable base sort and sorter functions for advanced use cases

Capabilities

Core Matching Function

The primary function for filtering and sorting arrays based on string matching.

/**
 * Takes an array of items and a value and returns a new array with the items that match the given value
 * @param items - the items to sort
 * @param value - the value to use for ranking  
 * @param options - configuration options for the sorter
 * @returns the new filtered and sorted array
 */
function matchSorter<ItemType = string>(
  items: ReadonlyArray<ItemType>,
  value: string,
  options?: MatchSorterOptions<ItemType>
): Array<ItemType>;

/** Rankings constants are also available as a property on the matchSorter function */
matchSorter.rankings: typeof rankings;

Usage Examples:

// Simple string matching
const fruits = ["apple", "apricot", "banana"];
matchSorter(fruits, "ap");
// Returns: ["apple", "apricot"] (both start with "ap")

// Case sensitivity and ranking
const items = ["Apple", "apple", "apply"];
matchSorter(items, "apple");
// Returns: ["apple", "Apple", "apply"] (exact match first, then case-insensitive, then starts-with)

// No matches below threshold
matchSorter(["hello", "world"], "xyz");
// Returns: [] (no items match "xyz")

// Using rankings via matchSorter property
const exactOnly = matchSorter(items, "search", {
  threshold: matchSorter.rankings.EQUAL
});

Ranking Constants

Predefined ranking values for different match quality levels.

const rankings: {
  readonly CASE_SENSITIVE_EQUAL: 7;
  readonly EQUAL: 6;
  readonly STARTS_WITH: 5;
  readonly WORD_STARTS_WITH: 4;
  readonly CONTAINS: 3;
  readonly ACRONYM: 2;
  readonly MATCHES: 1;
  readonly NO_MATCH: 0;
};

type Ranking = typeof rankings[keyof typeof rankings];

Ranking Quality Levels:

  • CASE_SENSITIVE_EQUAL (7): Exact match with same case
  • EQUAL (6): Exact match ignoring case
  • STARTS_WITH (5): String starts with search term
  • WORD_STARTS_WITH (4): A word in the string starts with search term
  • CONTAINS (3): String contains search term
  • ACRONYM (2): String's acronym matches search term (first letters of words and hyphen-separated parts)
  • MATCHES (1): Fuzzy match based on character proximity
  • NO_MATCH (0): No match found

Usage Examples:

import { matchSorter, rankings } from "match-sorter";

// Set minimum threshold to only return exact matches
const exactMatches = matchSorter(items, "search", {
  threshold: rankings.EQUAL
});

// Custom threshold per key
const users = [{ name: "John", bio: "Developer" }];
const results = matchSorter(users, "dev", {
  keys: [
    { key: "name", threshold: rankings.STARTS_WITH },
    { key: "bio", threshold: rankings.CONTAINS }
  ]
});

// Demonstrate ranking order
const cities = ["New York", "newark", "NEWARK", "Denver"];
matchSorter(cities, "newark");
// Returns: ["NEWARK", "newark", "New York"] 
// (case-sensitive first, then case-insensitive, then starts-with)

// Acronym matching example
const phrases = ["New York City", "National Youth Corp", "Not Your Cat"];
matchSorter(phrases, "nyc");
// Returns: ["New York City", "National Youth Corp", "Not Your Cat"]
// (all match the acronym "nyc")

Default Base Sort Function

Default sorting function used for tie-breaking when items have equal rankings.

/**
 * Default base sorting function for tie-breaking
 * Uses locale-aware string comparison on rankedValue
 * @param a - first ranked item to compare
 * @param b - second ranked item to compare  
 * @returns comparison result (-1, 0, 1)
 */
const defaultBaseSortFn: BaseSorter<unknown> = (a, b) =>
  String(a.rankedValue).localeCompare(String(b.rankedValue));

Configuration Options

The MatchSorterOptions interface provides comprehensive configuration for customizing match behavior. See the Types section for complete interface definitions.

Usage Examples:

// Object key matching
const books = [
  { title: "JavaScript Guide", author: "John Doe" },
  { title: "TypeScript Handbook", author: "Jane Smith" }
];

// Search multiple keys
matchSorter(books, "script", { keys: ["title", "author"] });
// Matches both books on title

// Nested property matching
const users = [
  { profile: { name: "Alice", bio: "Developer" } },
  { profile: { name: "Bob", bio: "Designer" } }
];
matchSorter(users, "alice", { keys: ["profile.name"] });

// Custom value getter
matchSorter(users, "dev", {
  keys: [(user) => [user.profile.name, user.profile.bio].join(" ")]
});

// Key-specific configuration
matchSorter(books, "doe", {
  keys: [
    { key: "title", threshold: rankings.STARTS_WITH },
    { key: "author", threshold: rankings.CONTAINS, maxRanking: rankings.EQUAL }
  ]
});

// Preserve diacritics
const names = ["José", "Jose", "María"];
matchSorter(names, "jose", { keepDiacritics: true });
// Returns: ["Jose"] (exact match only)

matchSorter(names, "jose", { keepDiacritics: false });
// Returns: ["Jose", "José"] (diacritics ignored)

// Custom sorter for reverse order
matchSorter(items, "search", {
  sorter: (rankedItems) => rankedItems.sort((a, b) => b.rank - a.rank)
});

// Custom base sort for tie-breaking
matchSorter(items, "search", {
  baseSort: (a, b) => a.index - b.index // Preserve original order
});

Advanced Nested Property Support

Support for complex nested property access patterns.

Nested Object Properties:

const data = [
  { user: { profile: { name: "Alice" } } },
  { user: { profile: { name: "Bob" } } }
];

matchSorter(data, "alice", { keys: ["user.profile.name"] });

Array Index Access:

const data = [
  { tags: ["javascript", "web"] },
  { tags: ["python", "data"] }
];

matchSorter(data, "web", { keys: ["tags.1"] }); // Search second tag

Wildcard Array Access:

const data = [
  { tags: ["red", "blue", "green"] },
  { tags: ["yellow", "purple"] }
];

matchSorter(data, "blue", { keys: ["tags.*"] }); // Search all array elements

Types

Exported Types

All types exported by match-sorter for TypeScript usage:

interface MatchSorterOptions<ItemType = unknown> {
  /** Array of keys to search when items are objects */
  keys?: ReadonlyArray<KeyOption<ItemType>>;
  /** Minimum ranking threshold for inclusion in results */
  threshold?: Ranking;
  /** Custom base sort function for tie-breaking */
  baseSort?: BaseSorter<ItemType>;
  /** Whether to preserve diacritics in string comparison */
  keepDiacritics?: boolean;
  /** Custom sorter function to replace default sorting logic */
  sorter?: Sorter<ItemType>;
}

interface KeyAttributesOptions<ItemType> {
  /** Property key or custom getter function */
  key?: string | ValueGetterKey<ItemType>;
  /** Minimum ranking threshold for this key */
  threshold?: Ranking;
  /** Maximum ranking this key can achieve */
  maxRanking?: Ranking;
  /** Minimum ranking this key can achieve */
  minRanking?: Ranking;
}

type KeyOption<ItemType> =
  | KeyAttributesOptions<ItemType>
  | ValueGetterKey<ItemType>
  | string;

interface KeyAttributes {
  /** Minimum ranking threshold for this key */
  threshold?: Ranking;
  /** Maximum ranking this key can achieve */
  maxRanking: Ranking;
  /** Minimum ranking this key can achieve */
  minRanking: Ranking;
}

interface RankingInfo {
  /** The string value that was ranked */
  rankedValue: string;
  /** The ranking score achieved */
  rank: Ranking;
  /** Index of the key that produced this ranking */
  keyIndex: number;
  /** Threshold setting for the matched key */
  keyThreshold: Ranking | undefined;
}

interface ValueGetterKey<ItemType> {
  /** Custom function to extract searchable values from items */
  (item: ItemType): string | Array<string>;
}

interface BaseSorter<ItemType> {
  /** Custom base sorting function for tie-breaking */
  (a: RankedItem<ItemType>, b: RankedItem<ItemType>): number;
}

interface Sorter<ItemType> {
  /** Custom sorter function that replaces default sorting logic */
  (matchItems: Array<RankedItem<ItemType>>): Array<RankedItem<ItemType>>;
}

interface RankedItem<ItemType> extends RankingInfo {
  /** The original item from the input array */
  item: ItemType;
  /** Original index of the item in the input array */
  index: number;
}