Skip to content

Commit

Permalink
feat(match): ignore diacritics when matching by default (#10)
Browse files Browse the repository at this point in the history
It's reasonable to consider this change breaking, but I think that it's
unlikely that anyone is depending on what I would consider a bug.

This adds our first dependency. Everything seems to be working with
bundledDependencies (tested locally) and the UMD builds.

Closes #8
  • Loading branch information
Kent C. Dodds authored Aug 30, 2016
1 parent 1cb4be5 commit 774ca9c
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 10 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ matchSorter(otherThings, 'app', {threshold: matchSorter.rankings.WORD_STARTS_WIT
* - MATCHES
* - NO_MATCH
*/

// **keepDiacritics** (defaults to false)
// by default, match-sorter will strip diacritics before doing any comparisons.
// this is the default because it makes the most sense from a UX perspective.
// You can disable this behavior by specifying keepDiacritics: false
const thingsWithDiacritics = ['jalapeño', 'à la carte', 'café', 'papier-mâché', 'à la mode']
matchSorter(thingsWithDiacritics, 'aa') // ['jalapeño', 'à la carte', 'papier-mâché', 'à la mode']
matchSorter(thingsWithDiacritics, 'aa', {keepDiacritics: true}) // ['jalapeño', 'à la carte']
matchSorter(thingsWithDiacritics, 'à', {keepDiacritics: true}) // ['à la carte', 'à la mode']
```

> In the examples above, we're using CommonJS. If you're using ES6 modules, then you can do:
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
"keywords": [],
"author": "Kent C. Dodds <[email protected]> (http://kentcdodds.com/)",
"license": "MIT",
"dependencies": {},
"bundledDependencies": [
"diacritic"
],
"dependencies": {
"diacritic": "0.0.2"
},
"devDependencies": {
"all-contributors-cli": "^3.0.0",
"ava": "^0.16.0",
Expand Down
49 changes: 40 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/**
* @name match-sorter
* @license MIT license.
* @copyright (c) 2016 Kent C. Dodds
* @author Kent C. Dodds <[email protected]>
*/
import diacritics from 'diacritic'

const rankings = {
EQUAL: 6,
STARTS_WITH: 5,
Expand All @@ -24,21 +32,29 @@ function matchSorter(items, value, options = {}) {
return matchedItems.sort(sortRankedItems).map(({item}) => item)

function reduceItemsToRanked(matches, item, index) {
const {rank, keyIndex} = getHighestRanking(item, keys, value)
const {rank, keyIndex} = getHighestRanking(item, keys, value, options)
if (rank >= threshold) {
matches.push({item, rank, index, keyIndex})
}
return matches
}
}

function getHighestRanking(item, keys, value) {
/**
* Gets the highest ranking for value for the given item based on its values for the given keys
* @param {*} item - the item to rank
* @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 {Number} - the highest ranking
*/
function getHighestRanking(item, keys, value, options) {
if (!keys) {
return {rank: getMatchRanking(item, value), keyIndex: -1}
return {rank: getMatchRanking(item, value, options), keyIndex: -1}
}
const valuesToRank = getAllValuesToRank(item, keys)
return valuesToRank.reduce(({rank, keyIndex}, itemValue, i) => {
const newRank = getMatchRanking(itemValue, value)
const newRank = getMatchRanking(itemValue, value, options)
if (newRank > rank) {
rank = newRank
keyIndex = i
Expand All @@ -51,12 +67,13 @@ function getHighestRanking(item, keys, value) {
* Gives a rankings score based on how well the two strings match.
* @param {String} testString - the string to test against
* @param {String} stringToRank - the string to rank
* @param {Object} options - options for the match (like keepDiacritics for comparison)
* @returns {Number} the ranking for how well stringToRank matches testString
*/
function getMatchRanking(testString, stringToRank) {
function getMatchRanking(testString, stringToRank, options) {
/* eslint complexity:[2, 8] */
testString = (`${testString}`).toLowerCase()
stringToRank = (`${stringToRank}`).toLowerCase()
testString = prepareValueForComparison(testString, options)
stringToRank = prepareValueForComparison(stringToRank, options)

// too long
if (stringToRank.length > testString.length) {
Expand Down Expand Up @@ -172,6 +189,20 @@ function sortRankedItems(a, b) {
}
}

/**
* Prepares value for comparison by stringifying it, removing diacritics (if specified), and toLowerCase-ing it
* @param {String} value - the value to clean
* @param {Object} options - {keepDiacritics: whether to remove diacritics}
* @return {String} the prepared value
*/
function prepareValueForComparison(value, {keepDiacritics}) {
value = `${value}` // toString
if (!keepDiacritics) {
value = diacritics.clean(value)
}
return value.toLowerCase()
}

/**
* Gets value for key in item at arbitrarily nested keypath
* @param {Object} item - the item
Expand All @@ -188,8 +219,8 @@ function getItemValue(item, key) {

/**
* Gets all the values for the given keys in the given item and returns an array of those values
* @param {Object} item - the item from which the values will be retrieved
* @param {Array} keys - the keys to use to retrieve the values
* @param {Object} item - the item from which the values will be retrieved
* @param {Array} keys - the keys to use to retrieve the values
* @return {Array} the values in an array
*/
function getAllValuesToRank(item, keys) {
Expand Down
19 changes: 19 additions & 0 deletions src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,25 @@ const tests = {
'app', 'apple', 'apply', 'fiji apple',
],
},
'defaults to ignore diacritics': {
input: [
['jalapeño', 'à la carte', 'café', 'papier-mâché', 'à la mode'],
'aa',
],
output: [
'jalapeño', 'à la carte', 'papier-mâché', 'à la mode',
],
},
'takes diacritics in account when keepDiacritics specified as true': {
input: [
['jalapeño', 'à la carte', 'papier-mâché', 'à la mode'],
'aa',
{keepDiacritics: true},
],
output: [
'jalapeño', 'à la carte',
],
},
}

Object.keys(tests).forEach(title => {
Expand Down

0 comments on commit 774ca9c

Please sign in to comment.