or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

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

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

```