0
# match-sorter
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: match-sorter
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install match-sorter`
10
11
## Core Imports
12
13
```typescript
14
// Main function and utilities
15
import { matchSorter, rankings, defaultBaseSortFn } from "match-sorter";
16
17
// Import with types (TypeScript)
18
import {
19
matchSorter,
20
rankings,
21
defaultBaseSortFn,
22
type MatchSorterOptions,
23
type KeyAttributesOptions,
24
type KeyOption,
25
type KeyAttributes,
26
type RankingInfo,
27
type ValueGetterKey
28
} from "match-sorter";
29
```
30
31
For CommonJS:
32
33
```javascript
34
const { matchSorter, rankings, defaultBaseSortFn } = require("match-sorter");
35
```
36
37
## Basic Usage
38
39
```typescript
40
import { matchSorter } from "match-sorter";
41
42
// Simple string array matching
43
const items = ["apple", "banana", "grape", "orange"];
44
const results = matchSorter(items, "ap");
45
// Returns: ["apple"] (starts with "ap")
46
47
// Object matching with keys
48
const users = [
49
{ name: "Sarah Connor", email: "sarah@example.com" },
50
{ name: "John Connor", email: "john@example.com" },
51
{ name: "Kyle Reese", email: "kyle@example.com" }
52
];
53
54
const matches = matchSorter(users, "connor", { keys: ["name"] });
55
// Returns: [{ name: "Sarah Connor", ... }, { name: "John Connor", ... }]
56
```
57
58
## Architecture
59
60
match-sorter uses a hierarchical ranking system that evaluates matches based on quality:
61
62
- **Rankings System**: Seven match quality levels from exact matches (highest) to fuzzy matches (lowest)
63
- **Key-based Matching**: Support for searching object properties, including nested properties
64
- **Configurable Thresholds**: Minimum match quality filtering with per-key customization
65
- **Accent Handling**: Optional diacritic removal for international text matching
66
- **Custom Sorting**: Pluggable base sort and sorter functions for advanced use cases
67
68
## Capabilities
69
70
### Core Matching Function
71
72
The primary function for filtering and sorting arrays based on string matching.
73
74
```typescript { .api }
75
/**
76
* Takes an array of items and a value and returns a new array with the items that match the given value
77
* @param items - the items to sort
78
* @param value - the value to use for ranking
79
* @param options - configuration options for the sorter
80
* @returns the new filtered and sorted array
81
*/
82
function matchSorter<ItemType = string>(
83
items: ReadonlyArray<ItemType>,
84
value: string,
85
options?: MatchSorterOptions<ItemType>
86
): Array<ItemType>;
87
88
/** Rankings constants are also available as a property on the matchSorter function */
89
matchSorter.rankings: typeof rankings;
90
```
91
92
**Usage Examples:**
93
94
```typescript
95
// Simple string matching
96
const fruits = ["apple", "apricot", "banana"];
97
matchSorter(fruits, "ap");
98
// Returns: ["apple", "apricot"] (both start with "ap")
99
100
// Case sensitivity and ranking
101
const items = ["Apple", "apple", "apply"];
102
matchSorter(items, "apple");
103
// Returns: ["apple", "Apple", "apply"] (exact match first, then case-insensitive, then starts-with)
104
105
// No matches below threshold
106
matchSorter(["hello", "world"], "xyz");
107
// Returns: [] (no items match "xyz")
108
109
// Using rankings via matchSorter property
110
const exactOnly = matchSorter(items, "search", {
111
threshold: matchSorter.rankings.EQUAL
112
});
113
```
114
115
### Ranking Constants
116
117
Predefined ranking values for different match quality levels.
118
119
```typescript { .api }
120
const rankings: {
121
readonly CASE_SENSITIVE_EQUAL: 7;
122
readonly EQUAL: 6;
123
readonly STARTS_WITH: 5;
124
readonly WORD_STARTS_WITH: 4;
125
readonly CONTAINS: 3;
126
readonly ACRONYM: 2;
127
readonly MATCHES: 1;
128
readonly NO_MATCH: 0;
129
};
130
131
type Ranking = typeof rankings[keyof typeof rankings];
132
```
133
134
**Ranking Quality Levels:**
135
136
- `CASE_SENSITIVE_EQUAL` (7): Exact match with same case
137
- `EQUAL` (6): Exact match ignoring case
138
- `STARTS_WITH` (5): String starts with search term
139
- `WORD_STARTS_WITH` (4): A word in the string starts with search term
140
- `CONTAINS` (3): String contains search term
141
- `ACRONYM` (2): String's acronym matches search term (first letters of words and hyphen-separated parts)
142
- `MATCHES` (1): Fuzzy match based on character proximity
143
- `NO_MATCH` (0): No match found
144
145
**Usage Examples:**
146
147
```typescript
148
import { matchSorter, rankings } from "match-sorter";
149
150
// Set minimum threshold to only return exact matches
151
const exactMatches = matchSorter(items, "search", {
152
threshold: rankings.EQUAL
153
});
154
155
// Custom threshold per key
156
const users = [{ name: "John", bio: "Developer" }];
157
const results = matchSorter(users, "dev", {
158
keys: [
159
{ key: "name", threshold: rankings.STARTS_WITH },
160
{ key: "bio", threshold: rankings.CONTAINS }
161
]
162
});
163
164
// Demonstrate ranking order
165
const cities = ["New York", "newark", "NEWARK", "Denver"];
166
matchSorter(cities, "newark");
167
// Returns: ["NEWARK", "newark", "New York"]
168
// (case-sensitive first, then case-insensitive, then starts-with)
169
170
// Acronym matching example
171
const phrases = ["New York City", "National Youth Corp", "Not Your Cat"];
172
matchSorter(phrases, "nyc");
173
// Returns: ["New York City", "National Youth Corp", "Not Your Cat"]
174
// (all match the acronym "nyc")
175
```
176
177
### Default Base Sort Function
178
179
Default sorting function used for tie-breaking when items have equal rankings.
180
181
```typescript { .api }
182
/**
183
* Default base sorting function for tie-breaking
184
* Uses locale-aware string comparison on rankedValue
185
* @param a - first ranked item to compare
186
* @param b - second ranked item to compare
187
* @returns comparison result (-1, 0, 1)
188
*/
189
const defaultBaseSortFn: BaseSorter<unknown> = (a, b) =>
190
String(a.rankedValue).localeCompare(String(b.rankedValue));
191
```
192
193
### Configuration Options
194
195
The `MatchSorterOptions` interface provides comprehensive configuration for customizing match behavior. See the [Types section](#types) for complete interface definitions.
196
197
**Usage Examples:**
198
199
```typescript
200
// Object key matching
201
const books = [
202
{ title: "JavaScript Guide", author: "John Doe" },
203
{ title: "TypeScript Handbook", author: "Jane Smith" }
204
];
205
206
// Search multiple keys
207
matchSorter(books, "script", { keys: ["title", "author"] });
208
// Matches both books on title
209
210
// Nested property matching
211
const users = [
212
{ profile: { name: "Alice", bio: "Developer" } },
213
{ profile: { name: "Bob", bio: "Designer" } }
214
];
215
matchSorter(users, "alice", { keys: ["profile.name"] });
216
217
// Custom value getter
218
matchSorter(users, "dev", {
219
keys: [(user) => [user.profile.name, user.profile.bio].join(" ")]
220
});
221
222
// Key-specific configuration
223
matchSorter(books, "doe", {
224
keys: [
225
{ key: "title", threshold: rankings.STARTS_WITH },
226
{ key: "author", threshold: rankings.CONTAINS, maxRanking: rankings.EQUAL }
227
]
228
});
229
230
// Preserve diacritics
231
const names = ["José", "Jose", "María"];
232
matchSorter(names, "jose", { keepDiacritics: true });
233
// Returns: ["Jose"] (exact match only)
234
235
matchSorter(names, "jose", { keepDiacritics: false });
236
// Returns: ["Jose", "José"] (diacritics ignored)
237
238
// Custom sorter for reverse order
239
matchSorter(items, "search", {
240
sorter: (rankedItems) => rankedItems.sort((a, b) => b.rank - a.rank)
241
});
242
243
// Custom base sort for tie-breaking
244
matchSorter(items, "search", {
245
baseSort: (a, b) => a.index - b.index // Preserve original order
246
});
247
```
248
249
### Advanced Nested Property Support
250
251
Support for complex nested property access patterns.
252
253
**Nested Object Properties:**
254
255
```typescript
256
const data = [
257
{ user: { profile: { name: "Alice" } } },
258
{ user: { profile: { name: "Bob" } } }
259
];
260
261
matchSorter(data, "alice", { keys: ["user.profile.name"] });
262
```
263
264
**Array Index Access:**
265
266
```typescript
267
const data = [
268
{ tags: ["javascript", "web"] },
269
{ tags: ["python", "data"] }
270
];
271
272
matchSorter(data, "web", { keys: ["tags.1"] }); // Search second tag
273
```
274
275
**Wildcard Array Access:**
276
277
```typescript
278
const data = [
279
{ tags: ["red", "blue", "green"] },
280
{ tags: ["yellow", "purple"] }
281
];
282
283
matchSorter(data, "blue", { keys: ["tags.*"] }); // Search all array elements
284
```
285
286
## Types
287
288
### Exported Types
289
290
All types exported by match-sorter for TypeScript usage:
291
292
```typescript { .api }
293
interface MatchSorterOptions<ItemType = unknown> {
294
/** Array of keys to search when items are objects */
295
keys?: ReadonlyArray<KeyOption<ItemType>>;
296
/** Minimum ranking threshold for inclusion in results */
297
threshold?: Ranking;
298
/** Custom base sort function for tie-breaking */
299
baseSort?: BaseSorter<ItemType>;
300
/** Whether to preserve diacritics in string comparison */
301
keepDiacritics?: boolean;
302
/** Custom sorter function to replace default sorting logic */
303
sorter?: Sorter<ItemType>;
304
}
305
306
interface KeyAttributesOptions<ItemType> {
307
/** Property key or custom getter function */
308
key?: string | ValueGetterKey<ItemType>;
309
/** Minimum ranking threshold for this key */
310
threshold?: Ranking;
311
/** Maximum ranking this key can achieve */
312
maxRanking?: Ranking;
313
/** Minimum ranking this key can achieve */
314
minRanking?: Ranking;
315
}
316
317
type KeyOption<ItemType> =
318
| KeyAttributesOptions<ItemType>
319
| ValueGetterKey<ItemType>
320
| string;
321
322
interface KeyAttributes {
323
/** Minimum ranking threshold for this key */
324
threshold?: Ranking;
325
/** Maximum ranking this key can achieve */
326
maxRanking: Ranking;
327
/** Minimum ranking this key can achieve */
328
minRanking: Ranking;
329
}
330
331
interface RankingInfo {
332
/** The string value that was ranked */
333
rankedValue: string;
334
/** The ranking score achieved */
335
rank: Ranking;
336
/** Index of the key that produced this ranking */
337
keyIndex: number;
338
/** Threshold setting for the matched key */
339
keyThreshold: Ranking | undefined;
340
}
341
342
interface ValueGetterKey<ItemType> {
343
/** Custom function to extract searchable values from items */
344
(item: ItemType): string | Array<string>;
345
}
346
347
interface BaseSorter<ItemType> {
348
/** Custom base sorting function for tie-breaking */
349
(a: RankedItem<ItemType>, b: RankedItem<ItemType>): number;
350
}
351
352
interface Sorter<ItemType> {
353
/** Custom sorter function that replaces default sorting logic */
354
(matchItems: Array<RankedItem<ItemType>>): Array<RankedItem<ItemType>>;
355
}
356
357
interface RankedItem<ItemType> extends RankingInfo {
358
/** The original item from the input array */
359
item: ItemType;
360
/** Original index of the item in the input array */
361
index: number;
362
}
363
```