Skip to content

Commit e7c9c20

Browse files
authored
feat: add matchSorterWithRankInfo to expose rank info in results (#169)
Closes #170
1 parent 6be7acb commit e7c9c20

3 files changed

Lines changed: 128 additions & 6 deletions

File tree

README.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,26 @@ matchSorter(list, 'y') // ['yo', 'hey']
103103
matchSorter(list, 'z') // []
104104
```
105105

106+
If you need the ranking metadata that `match-sorter` computes internally, use
107+
`matchSorterWithRankInfo`:
108+
109+
```javascript
110+
import {matchSorterWithRankInfo} from 'match-sorter'
111+
112+
const rankedResults = matchSorterWithRankInfo(list, 'h')
113+
// [
114+
// {
115+
// item: 'hello',
116+
// rankedValue: 'hello',
117+
// rank: 5,
118+
// keyIndex: -1,
119+
// keyThreshold: undefined,
120+
// index: 2,
121+
// },
122+
// // ...
123+
// ]
124+
```
125+
106126
## Advanced options
107127

108128
### keys: `[string]`
@@ -168,9 +188,9 @@ using dot-notation with the `*` wildcard instead of a numeric index.
168188

169189
```javascript
170190
const nestedObjList = [
171-
{aliases: [{name: {first: 'Janice'}},{name: {first: 'Jen'}}]},
172-
{aliases: [{name: {first: 'Fred'}},{name: {first: 'Frederic'}}]},
173-
{aliases: [{name: {first: 'George'}},{name: {first: 'Georgie'}}]},
191+
{aliases: [{name: {first: 'Janice'}}, {name: {first: 'Jen'}}]},
192+
{aliases: [{name: {first: 'Fred'}}, {name: {first: 'Frederic'}}]},
193+
{aliases: [{name: {first: 'George'}}, {name: {first: 'Georgie'}}]},
174194
]
175195
matchSorter(nestedObjList, 'jen', {keys: ['aliases.*.name.first']})
176196
// [{aliases: [{name: {first: 'Janice'}},{name: {first: 'Jen'}}]}]
@@ -355,13 +375,15 @@ _You can customize the core sorting behavior by specifying a custom `sorter`
355375
function:_
356376

357377
Disable sorting entirely:
378+
358379
```javascript
359380
const list = ['appl', 'C apple', 'B apple', 'A apple', 'app', 'applebutter']
360381
matchSorter(list, 'apple', {sorter: rankedItems => rankedItems})
361382
// ['C apple', 'B apple', 'A apple', 'applebutter']
362383
```
363384

364385
Return the unsorted rankedItems, but in reverse order:
386+
365387
```javascript
366388
const list = ['appl', 'C apple', 'B apple', 'A apple', 'app', 'applebutter']
367389
matchSorter(list, 'apple', {sorter: rankedItems => [...rankedItems].reverse()})

src/__tests__/index.ts

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import {matchSorter, rankings, MatchSorterOptions} from '../'
1+
import {
2+
matchSorter,
3+
matchSorterWithRankInfo,
4+
rankings,
5+
MatchSorterOptions,
6+
type RankedItem,
7+
} from '../'
28

39
type TestCase = {
410
input: [Array<unknown>, string, MatchSorterOptions?]
@@ -663,6 +669,69 @@ for (const [
663669
}
664670
}
665671

672+
test('can return ranked items with ranking metadata attached', () => {
673+
const rankedResults: Array<RankedItem<{tea: string; alias: string}>> =
674+
matchSorterWithRankInfo(
675+
[
676+
{tea: 'Earl Grey', alias: 'A'},
677+
{tea: 'Assam', alias: 'B'},
678+
{tea: 'Black', alias: 'C'},
679+
],
680+
'A',
681+
{
682+
keys: ['tea', {maxRanking: rankings.STARTS_WITH, key: 'alias'}],
683+
},
684+
)
685+
686+
expect(rankedResults).toEqual([
687+
{
688+
item: {tea: 'Assam', alias: 'B'},
689+
rankedValue: 'Assam',
690+
rank: rankings.STARTS_WITH,
691+
keyIndex: 0,
692+
keyThreshold: undefined,
693+
index: 1,
694+
},
695+
{
696+
item: {tea: 'Earl Grey', alias: 'A'},
697+
rankedValue: 'A',
698+
rank: rankings.STARTS_WITH,
699+
keyIndex: 1,
700+
keyThreshold: undefined,
701+
index: 0,
702+
},
703+
{
704+
item: {tea: 'Black', alias: 'C'},
705+
rankedValue: 'Black',
706+
rank: rankings.CONTAINS,
707+
keyIndex: 0,
708+
keyThreshold: undefined,
709+
index: 2,
710+
},
711+
])
712+
})
713+
714+
test('can return ranked items without passing options', () => {
715+
expect(matchSorterWithRankInfo(['hello', 'hey', 'sup'], 'h')).toEqual([
716+
{
717+
item: 'hello',
718+
rankedValue: 'hello',
719+
rank: rankings.STARTS_WITH,
720+
keyIndex: -1,
721+
keyThreshold: undefined,
722+
index: 0,
723+
},
724+
{
725+
item: 'hey',
726+
rankedValue: 'hey',
727+
rank: rankings.STARTS_WITH,
728+
keyIndex: -1,
729+
keyThreshold: undefined,
730+
index: 1,
731+
},
732+
])
733+
})
734+
666735
/*
667736
eslint
668737
jest/prefer-each: "off",

src/index.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,29 @@ function matchSorter<ItemType = string>(
8484
value: string,
8585
options: MatchSorterOptions<ItemType> = {},
8686
): Array<ItemType> {
87+
return getRankedItems(items, value, options).map(({item}) => item)
88+
}
89+
90+
/**
91+
* Takes an array of items and a value and returns ranked items with metadata attached
92+
* @param {Array} items - the items to sort
93+
* @param {String} value - the value to use for ranking
94+
* @param {Object} options - Some options to configure the sorter
95+
* @return {Array} - the new ranked array
96+
*/
97+
function matchSorterWithRankInfo<ItemType = string>(
98+
items: ReadonlyArray<ItemType>,
99+
value: string,
100+
options: MatchSorterOptions<ItemType> = {},
101+
): Array<RankedItem<ItemType>> {
102+
return getRankedItems(items, value, options)
103+
}
104+
105+
function getRankedItems<ItemType = string>(
106+
items: ReadonlyArray<ItemType>,
107+
value: string,
108+
options: MatchSorterOptions<ItemType>,
109+
): Array<RankedItem<ItemType>> {
87110
const {
88111
keys,
89112
threshold = rankings.MATCHES,
@@ -92,7 +115,7 @@ function matchSorter<ItemType = string>(
92115
matchedItems.sort((a, b) => sortRankedValues(a, b, baseSort)),
93116
} = options
94117
const matchedItems = items.reduce(reduceItemsToRanked, [])
95-
return sorter(matchedItems).map(({item}) => item)
118+
return sorter(matchedItems)
96119

97120
function reduceItemsToRanked(
98121
matches: Array<RankedItem<ItemType>>,
@@ -109,6 +132,7 @@ function matchSorter<ItemType = string>(
109132
}
110133

111134
matchSorter.rankings = rankings
135+
matchSorterWithRankInfo.rankings = rankings
112136

113137
/**
114138
* Gets the highest ranking for value for the given item based on its values for the given keys
@@ -520,14 +544,21 @@ function getKeyAttributes<ItemType>(key: KeyOption<ItemType>): KeyAttributes {
520544
return {...defaultKeyAttributes, ...key}
521545
}
522546

523-
export {matchSorter, rankings, defaultBaseSortFn, getItemValues}
547+
export {
548+
matchSorter,
549+
matchSorterWithRankInfo,
550+
rankings,
551+
defaultBaseSortFn,
552+
getItemValues,
553+
}
524554

525555
export type {
526556
MatchSorterOptions,
527557
KeyAttributesOptions,
528558
KeyOption,
529559
KeyAttributes,
530560
RankingInfo,
561+
RankedItem,
531562
ValueGetterKey,
532563
}
533564

0 commit comments

Comments
 (0)