Permalink
Browse files

feat: add ability to set a threshold per key (#50)

<!--
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 kentcdodds committed Aug 29, 2018
1 parent 5b79383 commit 22f1d7300ba3bd8d74c4ae0a717d678150490124
Showing with 84 additions and 34 deletions.
  1. +11 −0 .all-contributorsrc
  2. +34 −26 README.md
  3. +21 −0 src/__tests__/index.js
  4. +18 −8 src/index.js
View
@@ -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"
View
@@ -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]
@@ -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 -->
@@ -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
@@ -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']
@@ -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 -->
View
@@ -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 => {
View
@@ -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
@@ -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) {
@@ -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},
)
}

0 comments on commit 22f1d73

Please sign in to comment.