Skip to content

Commit

Permalink
feat: add ability to set a threshold per key (#50)
Browse files Browse the repository at this point in the history
<!--
Thanks for your interest in the project. Bugs filed and PRs submitted are appreciated!

Please make sure that you are familiar with and follow the Code of Conduct for
this project (found in the CODE_OF_CONDUCT.md file).

Also, please make sure you're familiar with and follow the instructions in the
contributing guidelines (found in the CONTRIBUTING.md file).

If you're new to contributing to open source projects, you might find this free
video course helpful: http://kcd.im/pull-request

Please fill out the information below to expedite the review and (hopefully)
merge of your pull request!
-->

<!-- What changes are being made? (What feature/bug is being fixed here?) -->

**What**:
Add the ability to set a threshold per key
<!-- Why are these changes necessary? -->

**Why**:
To be able to set specific keys to only match when they meet a specified threshold
<!-- How were these changes implemented? -->

**How**:
By adding returning the keys threshold with each match and verifying that the the match is `>=` the threshold specified on its key.
<!-- Have you done all of these things?  -->

**Checklist**:

<!-- add "N/A" to the end of each line that's irrelevant to your changes -->

<!-- to check an item, place an "x" in the box like so: "- [x] Documentation" -->

* [x] Documentation
* [x] Tests
* [x] Ready to be merged <!-- In your opinion, is this ready to be merged as soon as it's reviewed? -->
* [x] Added myself to contributors table <!-- this is optional, see the contributing guidelines for instructions -->

<!-- feel free to add additional comments -->
  • Loading branch information
tikotzky authored and Kent C. Dodds committed Aug 29, 2018
1 parent 5b79383 commit 22f1d73
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 34 deletions.
11 changes: 11 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@
"contributions": [
"code"
]
},
{
"login": "tikotzky",
"name": "Mordy Tikotzky",
"avatar_url": "https://avatars3.githubusercontent.com/u/200528?v=4",
"profile": "https://github.com/tikotzky",
"contributions": [
"code",
"doc",
"test"
]
}
],
"repoType": "github"
Expand Down
60 changes: 34 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
[![downloads][downloads-badge]][npm-stat]
[![MIT License][license-badge]][license]

[![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors)
[![PRs Welcome][prs-badge]][prs]
[![Donate][donate-badge]][donate]
[![Code of Conduct][coc-badge]][coc]
Expand Down Expand Up @@ -54,21 +54,20 @@ To explain the ranking system, I'll use countries as an example:
This ranking seems to make sense in people's minds. At least it does in mine. Feedback welcome!

<!-- START doctoc generated TOC please keep comment here to allow auto update -->

<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

* [Getting Started](#getting-started)
* [Installation](#installation)
* [Usage](#usage)
* [Advanced options](#advanced-options)
* [keys: `[string]`](#keys-string)
* [threshold: `number`](#threshold-number)
* [keepDiacritics: `boolean`](#keepdiacritics-boolean)
* [Using ES6?](#using-es6)
* [Inspiration](#inspiration)
* [Other Solutions](#other-solutions)
* [Contributors](#contributors)
* [LICENSE](#license)
- [Getting Started](#getting-started)
- [Installation](#installation)
- [Usage](#usage)
- [Advanced options](#advanced-options)
- [keys: `[string]`](#keys-string)
- [threshold: `number`](#threshold-number)
- [keepDiacritics: `boolean`](#keepdiacritics-boolean)
- [Using ES6?](#using-es6)
- [Inspiration](#inspiration)
- [Other Solutions](#other-solutions)
- [Contributors](#contributors)
- [LICENSE](#license)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

Expand Down Expand Up @@ -160,6 +159,16 @@ matchSorter(list, 'j', {keys: [item => item.name]})
// [{name: 'Janice'}, {name: 'Jen'}]
```

**Threshold**: You may specify an individual threshold for specific keys. A key will only match if it meets the specified threshold. _For more information regarding thresholds [see below](#threshold-number)_

```javascript
const list = [{name: 'Fred', color: 'Orange'}, {name: 'Jen', color: 'Red'}]
matchSorter(list, 'ed', {
keys: [{threshold: rankings.STARTS_WITH, key: 'name'}, 'color'],
})
//[{name: 'Jen', color: 'Red'}]
```

**Min and Max Ranking**: You may restrict specific keys to a minimum or maximum ranking by passing in an object. A key with a minimum rank will only get promoted if there is at least a simple match.

```javascript
Expand Down Expand Up @@ -198,16 +207,16 @@ _Default: `MATCHES`_
Thresholds can be used to specify the criteria used to rank the results.
Available thresholds (from top to bottom) are:

* CASE_SENSITIVE_EQUAL
* EQUAL
* STARTS_WITH
* WORD_STARTS_WITH
* STRING_CASE
* STRING_CASE_ACRONYM
* CONTAINS
* ACRONYM
* MATCHES _(default value)_
* NO_MATCH
- CASE_SENSITIVE_EQUAL
- EQUAL
- STARTS_WITH
- WORD_STARTS_WITH
- STRING_CASE
- STRING_CASE_ACRONYM
- CONTAINS
- ACRONYM
- MATCHES _(default value)_
- NO_MATCH

```javascript
const fruit = ['orange', 'apple', 'grape', 'banana']
Expand Down Expand Up @@ -269,11 +278,10 @@ You might try [Fuse.js](https://github.com/krisk/Fuse). It uses advanced math fa
Thanks goes to these people ([emoji key][emojis]):

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->

<!-- prettier-ignore -->
| [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub><b>Kent C. Dodds</b></sub>](https://kentcdodds.com)<br />[💻](https://github.com/kentcdodds/match-sorter/commits?author=kentcdodds "Code") [📖](https://github.com/kentcdodds/match-sorter/commits?author=kentcdodds "Documentation") [🚇](#infra-kentcdodds "Infrastructure (Hosting, Build-Tools, etc)") [⚠️](https://github.com/kentcdodds/match-sorter/commits?author=kentcdodds "Tests") [👀](#review-kentcdodds "Reviewed Pull Requests") | [<img src="https://avatars.githubusercontent.com/u/8263298?v=3" width="100px;"/><br /><sub><b>Conor Hastings</b></sub>](http://conorhastings.com)<br />[💻](https://github.com/kentcdodds/match-sorter/commits?author=conorhastings "Code") [📖](https://github.com/kentcdodds/match-sorter/commits?author=conorhastings "Documentation") [⚠️](https://github.com/kentcdodds/match-sorter/commits?author=conorhastings "Tests") [👀](#review-conorhastings "Reviewed Pull Requests") | [<img src="https://avatars.githubusercontent.com/u/574806?v=3" width="100px;"/><br /><sub><b>Rogelio Guzman</b></sub>](https://github.com/rogeliog)<br />[📖](https://github.com/kentcdodds/match-sorter/commits?author=rogeliog "Documentation") | [<img src="https://avatars.githubusercontent.com/u/1416436?v=3" width="100px;"/><br /><sub><b>Claudéric Demers</b></sub>](http://ced.io)<br />[💻](https://github.com/kentcdodds/match-sorter/commits?author=clauderic "Code") [📖](https://github.com/kentcdodds/match-sorter/commits?author=clauderic "Documentation") [⚠️](https://github.com/kentcdodds/match-sorter/commits?author=clauderic "Tests") | [<img src="https://avatars3.githubusercontent.com/u/4150097?v=3" width="100px;"/><br /><sub><b>Kevin Davis</b></sub>](kevindav.us)<br />[💻](https://github.com/kentcdodds/match-sorter/commits?author=osfan501 "Code") [⚠️](https://github.com/kentcdodds/match-sorter/commits?author=osfan501 "Tests") | [<img src="https://avatars1.githubusercontent.com/u/19157735?v=3" width="100px;"/><br /><sub><b>Denver Chen</b></sub>](https://github.com/nfdjps)<br />[💻](https://github.com/kentcdodds/match-sorter/commits?author=nfdjps "Code") [📖](https://github.com/kentcdodds/match-sorter/commits?author=nfdjps "Documentation") [⚠️](https://github.com/kentcdodds/match-sorter/commits?author=nfdjps "Tests") | [<img src="https://avatars0.githubusercontent.com/u/12719057?v=4" width="100px;"/><br /><sub><b>Christian Ruigrok</b></sub>](http://ruigrok.info)<br />[🐛](https://github.com/kentcdodds/match-sorter/issues?q=author%3AChrisRu "Bug reports") [💻](https://github.com/kentcdodds/match-sorter/commits?author=ChrisRu "Code") [📖](https://github.com/kentcdodds/match-sorter/commits?author=ChrisRu "Documentation") |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<img src="https://avatars1.githubusercontent.com/u/2084833?v=4" width="100px;"/><br /><sub><b>Hozefa</b></sub>](https://github.com/hozefaj)<br />[🐛](https://github.com/kentcdodds/match-sorter/issues?q=author%3Ahozefaj "Bug reports") [💻](https://github.com/kentcdodds/match-sorter/commits?author=hozefaj "Code") [⚠️](https://github.com/kentcdodds/match-sorter/commits?author=hozefaj "Tests") [🤔](#ideas-hozefaj "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/9403361?v=4" width="100px;"/><br /><sub><b>pushpinder107</b></sub>](https://github.com/pushpinder107)<br />[💻](https://github.com/kentcdodds/match-sorter/commits?author=pushpinder107 "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/2084833?v=4" width="100px;"/><br /><sub><b>Hozefa</b></sub>](https://github.com/hozefaj)<br />[🐛](https://github.com/kentcdodds/match-sorter/issues?q=author%3Ahozefaj "Bug reports") [💻](https://github.com/kentcdodds/match-sorter/commits?author=hozefaj "Code") [⚠️](https://github.com/kentcdodds/match-sorter/commits?author=hozefaj "Tests") [🤔](#ideas-hozefaj "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/9403361?v=4" width="100px;"/><br /><sub><b>pushpinder107</b></sub>](https://github.com/pushpinder107)<br />[💻](https://github.com/kentcdodds/match-sorter/commits?author=pushpinder107 "Code") | [<img src="https://avatars3.githubusercontent.com/u/200528?v=4" width="100px;"/><br /><sub><b>Mordy Tikotzky</b></sub>](https://github.com/tikotzky)<br />[💻](https://github.com/kentcdodds/match-sorter/commits?author=tikotzky "Code") [📖](https://github.com/kentcdodds/match-sorter/commits?author=tikotzky "Documentation") [⚠️](https://github.com/kentcdodds/match-sorter/commits?author=tikotzky "Tests") |

<!-- ALL-CONTRIBUTORS-LIST:END -->

Expand Down
21 changes: 21 additions & 0 deletions src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,27 @@ const tests = {
{tea: 'Green', alias: 'C'},
],
},
'only match when key meets threshold': {
input: [
[{name: 'Fred', color: 'Orange'}, {name: 'Jen', color: 'Red'}],
'ed',
{
keys: [{threshold: rankings.STARTS_WITH, key: 'name'}, 'color'],
},
],
output: [{name: 'Jen', color: 'Red'}],
},
'should match when key threshold is lower than the default threshold': {
input: [
[{name: 'Fred', color: 'Orange'}, {name: 'Jen', color: 'Red'}],
'ed',
{
keys: ['name', {threshold: rankings.CONTAINS, key: 'color'}],
threshold: rankings.STARTS_WITH,
},
],
output: [{name: 'Jen', color: 'Red'}],
},
}

Object.keys(tests).forEach(title => {
Expand Down
26 changes: 18 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,13 @@ function matchSorter(items, value, options = {}) {
return matchedItems.sort(sortRankedItems).map(({item}) => item)

function reduceItemsToRanked(matches, item, index) {
const {rank, keyIndex} = getHighestRanking(item, keys, value, options)
if (rank >= threshold) {
const {rank, keyIndex, keyThreshold = threshold} = getHighestRanking(
item,
keys,
value,
options,
)
if (rank >= keyThreshold) {
matches.push({item, rank, index, keyIndex})
}
return matches
Expand All @@ -60,17 +65,21 @@ function matchSorter(items, value, options = {}) {
* @param {Array} keys - the keys to get values from the item for the ranking
* @param {String} value - the value to rank against
* @param {Object} options - options to control the ranking
* @return {{rank: Number, keyIndex: Number}} - the highest ranking
* @return {{rank: Number, keyIndex: Number, keyThreshold: Number}} - the highest ranking
*/
function getHighestRanking(item, keys, value, options) {
if (!keys) {
return {rank: getMatchRanking(item, value, options), keyIndex: -1}
return {
rank: getMatchRanking(item, value, options),
keyIndex: -1,
keyThreshold: options.threshold,
}
}
const valuesToRank = getAllValuesToRank(item, keys)
return valuesToRank.reduce(
({rank, keyIndex}, {itemValue, attributes}, i) => {
({rank, keyIndex, keyThreshold}, {itemValue, attributes}, i) => {
let newRank = getMatchRanking(itemValue, value, options)
const {minRanking, maxRanking} = attributes
const {minRanking, maxRanking, threshold} = attributes
if (newRank < minRanking && newRank >= rankings.MATCHES) {
newRank = minRanking
} else if (newRank > maxRanking) {
Expand All @@ -79,10 +88,11 @@ function getHighestRanking(item, keys, value, options) {
if (newRank > rank) {
rank = newRank
keyIndex = i
keyThreshold = threshold
}
return {rank, keyIndex}
return {rank, keyIndex, keyThreshold}
},
{rank: rankings.NO_MATCH, keyIndex: -1},
{rank: rankings.NO_MATCH, keyIndex: -1, keyThreshold: options.threshold},
)
}

Expand Down

0 comments on commit 22f1d73

Please sign in to comment.