landvex: Fixar och tester klara för alla komponenter
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2020 Kent C. Dodds
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+542
@@ -0,0 +1,542 @@
|
||||
<div align="center">
|
||||
<h1>match-sorter</h1>
|
||||
|
||||
<p>Simple, expected, and deterministic best-match sorting of an array in JavaScript</p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
**[Demo](https://codesandbox.io/s/wyk856yo48)**
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[![Build Status][build-badge]][build]
|
||||
[![Code Coverage][coverage-badge]][coverage]
|
||||
[![version][version-badge]][package]
|
||||
[![downloads][downloads-badge]][npmtrends]
|
||||
[![MIT License][license-badge]][license]
|
||||
[![All Contributors][all-contributors-badge]](#contributors-)
|
||||
[![PRs Welcome][prs-badge]][prs]
|
||||
[![Code of Conduct][coc-badge]][coc]
|
||||
[![Examples][examples-badge]][examples]
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## The problem
|
||||
|
||||
1. You have a list of dozens, hundreds, or thousands of items
|
||||
2. You want to filter and sort those items intelligently (maybe you have a
|
||||
filter input for the user)
|
||||
3. You want simple, expected, and deterministic sorting of the items (no fancy
|
||||
math algorithm that fancily changes the sorting as they type)
|
||||
|
||||
## This solution
|
||||
|
||||
This follows a simple and sensible (user friendly) algorithm that makes it easy
|
||||
for you to filter and sort a list of items based on given input. Items are
|
||||
ranked based on sensible criteria that result in a better user experience.
|
||||
|
||||
To explain the ranking system, I'll use countries as an example:
|
||||
|
||||
1. **CASE SENSITIVE EQUALS**: Case-sensitive equality trumps all. These will be
|
||||
first. (ex. `France` would match `France`, but not `france`)
|
||||
2. **EQUALS**: Case-insensitive equality (ex. `France` would match `france`)
|
||||
3. **STARTS WITH**: If the item starts with the given value (ex. `Sou` would
|
||||
match `South Korea` or `South Africa`)
|
||||
4. **WORD STARTS WITH**: If the item has multiple words, then if one of those
|
||||
words starts with the given value (ex. `Repub` would match
|
||||
`Dominican Republic`)
|
||||
5. **CONTAINS**: If the item contains the given value (ex. `ham` would match
|
||||
`Bahamas`)
|
||||
6. **ACRONYM**: If the item's acronym is the given value (ex. `us` would match
|
||||
`United States`)
|
||||
7. **SIMPLE MATCH**: If the item has letters in the same order as the letters
|
||||
of the given value (ex. `iw` would match `Zimbabwe`, but not `Kuwait`
|
||||
because it must be in the same order). Furthermore, if the item is a closer
|
||||
match, it will rank higher (ex. `ua` matches `Uruguay` more closely than
|
||||
`United States of America`, therefore `Uruguay` will be ordered before
|
||||
`United States of America`)
|
||||
|
||||
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 -->
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Advanced options](#advanced-options)
|
||||
- [keys: `[string]`](#keys-string)
|
||||
- [threshold: `number`](#threshold-number)
|
||||
- [keepDiacritics: `boolean`](#keepdiacritics-boolean)
|
||||
- [baseSort: `function(itemA, itemB): -1 | 0 | 1`](#basesort-functionitema-itemb--1--0--1)
|
||||
- [sorter: `function(rankedItems): rankedItems`](#sorter-functionrankeditems-rankeditems)
|
||||
- [Recipes](#recipes)
|
||||
- [Match PascalCase, camelCase, snake_case, or kebab-case as words](#match-pascalcase-camelcase-snake_case-or-kebab-case-as-words)
|
||||
- [Match many words across multiple fields (table filtering)](#match-many-words-across-multiple-fields-table-filtering)
|
||||
- [Inspiration](#inspiration)
|
||||
- [Other Solutions](#other-solutions)
|
||||
- [Issues](#issues)
|
||||
- [🐛 Bugs](#-bugs)
|
||||
- [💡 Feature Requests](#-feature-requests)
|
||||
- [Contributors ✨](#contributors-)
|
||||
- [LICENSE](#license)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
## Installation
|
||||
|
||||
This module is distributed via [npm][npm] which is bundled with [node][node] and
|
||||
should be installed as one of your project's `dependencies`:
|
||||
|
||||
```
|
||||
npm install match-sorter
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
import {matchSorter} from 'match-sorter'
|
||||
// or const {matchSorter} = require('match-sorter')
|
||||
// or window.matchSorter.matchSorter
|
||||
const list = ['hi', 'hey', 'hello', 'sup', 'yo']
|
||||
matchSorter(list, 'h') // ['hello', 'hey', 'hi']
|
||||
matchSorter(list, 'y') // ['yo', 'hey']
|
||||
matchSorter(list, 'z') // []
|
||||
```
|
||||
|
||||
## Advanced options
|
||||
|
||||
### keys: `[string]`
|
||||
|
||||
_Default: `undefined`_
|
||||
|
||||
By default it just uses the value itself as above. Passing an array tells
|
||||
match-sorter which keys to use for the ranking.
|
||||
|
||||
```javascript
|
||||
const objList = [
|
||||
{name: 'Janice', color: 'Green'},
|
||||
{name: 'Fred', color: 'Orange'},
|
||||
{name: 'George', color: 'Blue'},
|
||||
{name: 'Jen', color: 'Red'},
|
||||
]
|
||||
matchSorter(objList, 'g', {keys: ['name', 'color']})
|
||||
// [{name: 'George', color: 'Blue'}, {name: 'Janice', color: 'Green'}, {name: 'Fred', color: 'Orange'}]
|
||||
|
||||
matchSorter(objList, 're', {keys: ['color', 'name']})
|
||||
// [{name: 'Jen', color: 'Red'}, {name: 'Janice', color: 'Green'}, {name: 'Fred', color: 'Orange'}, {name: 'George', color: 'Blue'}]
|
||||
```
|
||||
|
||||
**Array of values**: When the specified key matches an array of values, the best
|
||||
match from the values of in the array is going to be used for the ranking.
|
||||
|
||||
```javascript
|
||||
const iceCreamYum = [
|
||||
{favoriteIceCream: ['mint', 'chocolate']},
|
||||
{favoriteIceCream: ['candy cane', 'brownie']},
|
||||
{favoriteIceCream: ['birthday cake', 'rocky road', 'strawberry']},
|
||||
]
|
||||
matchSorter(iceCreamYum, 'cc', {keys: ['favoriteIceCream']})
|
||||
// [{favoriteIceCream: ['candy cane', 'brownie']}, {favoriteIceCream: ['mint', 'chocolate']}]
|
||||
```
|
||||
|
||||
**Nested Keys**: You can specify nested keys using dot-notation.
|
||||
|
||||
```javascript
|
||||
const nestedObjList = [
|
||||
{name: {first: 'Janice'}},
|
||||
{name: {first: 'Fred'}},
|
||||
{name: {first: 'George'}},
|
||||
{name: {first: 'Jen'}},
|
||||
]
|
||||
matchSorter(nestedObjList, 'j', {keys: ['name.first']})
|
||||
// [{name: {first: 'Janice'}}, {name: {first: 'Jen'}}]
|
||||
|
||||
const nestedObjList = [
|
||||
{name: [{first: 'Janice'}]},
|
||||
{name: [{first: 'Fred'}]},
|
||||
{name: [{first: 'George'}]},
|
||||
{name: [{first: 'Jen'}]},
|
||||
]
|
||||
matchSorter(nestedObjList, 'j', {keys: ['name.0.first']})
|
||||
// [{name: {first: 'Janice'}}, {name: {first: 'Jen'}}]
|
||||
|
||||
// matchSorter(nestedObjList, 'j', {keys: ['name[0].first']}) does not work
|
||||
```
|
||||
|
||||
This even works with arrays of multiple nested objects: just specify the key
|
||||
using dot-notation with the `*` wildcard instead of a numeric index.
|
||||
|
||||
```javascript
|
||||
const nestedObjList = [
|
||||
{aliases: [{name: {first: 'Janice'}},{name: {first: 'Jen'}}]},
|
||||
{aliases: [{name: {first: 'Fred'}},{name: {first: 'Frederic'}}]},
|
||||
{aliases: [{name: {first: 'George'}},{name: {first: 'Georgie'}}]},
|
||||
]
|
||||
matchSorter(nestedObjList, 'jen', {keys: ['aliases.*.name.first']})
|
||||
// [{aliases: [{name: {first: 'Janice'}},{name: {first: 'Jen'}}]}]
|
||||
matchSorter(nestedObjList, 'jen', {keys: ['aliases.0.name.first']})
|
||||
// []
|
||||
```
|
||||
|
||||
**Property Callbacks**: Alternatively, you may also pass in a callback function
|
||||
that resolves the value of the key(s) you wish to match on. This is especially
|
||||
useful when interfacing with libraries such as Immutable.js
|
||||
|
||||
```javascript
|
||||
const list = [{name: 'Janice'}, {name: 'Fred'}, {name: 'George'}, {name: 'Jen'}]
|
||||
matchSorter(list, 'j', {keys: [item => item.name]})
|
||||
// [{name: 'Janice'}, {name: 'Jen'}]
|
||||
```
|
||||
|
||||
For more complex structures, expanding on the `nestedObjList` example above, you
|
||||
can use `map`:
|
||||
|
||||
```javascript
|
||||
const nestedObjList = [
|
||||
{
|
||||
name: [
|
||||
{first: 'Janice', last: 'Smith'},
|
||||
{first: 'Jon', last: 'Doe'},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: [
|
||||
{first: 'Fred', last: 'Astaire'},
|
||||
{first: 'Jenny', last: 'Doe'},
|
||||
{first: 'Wilma', last: 'Flintstone'},
|
||||
],
|
||||
},
|
||||
]
|
||||
matchSorter(nestedObjList, 'doe', {
|
||||
keys: [
|
||||
item => item.name.map(i => i.first),
|
||||
item => item.name.map(i => i.last),
|
||||
],
|
||||
})
|
||||
// [name: [{ first: 'Janice', last: 'Smith' },{ first: 'Jon', last: 'Doe' }], name: [{ first: 'Fred', last: 'Astaire' },{ first: 'Jenny', last: 'Doe' },{ first: 'Wilma', last: 'Flintstone' }]]
|
||||
```
|
||||
|
||||
**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: matchSorter.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
|
||||
const tea = [
|
||||
{tea: 'Earl Grey', alias: 'A'},
|
||||
{tea: 'Assam', alias: 'B'},
|
||||
{tea: 'Black', alias: 'C'},
|
||||
]
|
||||
matchSorter(tea, 'A', {
|
||||
keys: ['tea', {maxRanking: matchSorter.rankings.STARTS_WITH, key: 'alias'}],
|
||||
})
|
||||
// without maxRanking, Earl Grey would come first because the alias "A" would be CASE_SENSITIVE_EQUAL
|
||||
// `tea` key comes before `alias` key, so Assam comes first even though both match as STARTS_WITH
|
||||
// [{tea: 'Assam', alias: 'B'}, {tea: 'Earl Grey', alias: 'A'},{tea: 'Black', alias: 'C'}]
|
||||
```
|
||||
|
||||
```javascript
|
||||
const tea = [
|
||||
{tea: 'Milk', alias: 'moo'},
|
||||
{tea: 'Oolong', alias: 'B'},
|
||||
{tea: 'Green', alias: 'C'},
|
||||
]
|
||||
matchSorter(tea, 'oo', {
|
||||
keys: ['tea', {minRanking: matchSorter.rankings.EQUAL, key: 'alias'}],
|
||||
})
|
||||
// minRanking bumps Milk up to EQUAL from CONTAINS (alias)
|
||||
// Oolong matches as STARTS_WITH
|
||||
// Green is missing due to no match
|
||||
// [{tea: 'Milk', alias: 'moo'}, {tea: 'Oolong', alias: 'B'}]
|
||||
```
|
||||
|
||||
### threshold: `number`
|
||||
|
||||
_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
|
||||
- CONTAINS
|
||||
- ACRONYM
|
||||
- MATCHES _(default value)_
|
||||
- NO_MATCH
|
||||
|
||||
```javascript
|
||||
const fruit = ['orange', 'apple', 'grape', 'banana']
|
||||
matchSorter(fruit, 'ap', {threshold: matchSorter.rankings.NO_MATCH})
|
||||
// ['apple', 'grape', 'orange', 'banana'] (returns all items, just sorted by best match)
|
||||
|
||||
const things = ['google', 'airbnb', 'apple', 'apply', 'app'],
|
||||
matchSorter(things, 'app', {threshold: matchSorter.rankings.EQUAL})
|
||||
// ['app'] (only items that are equal)
|
||||
|
||||
const otherThings = ['fiji apple', 'google', 'app', 'crabapple', 'apple', 'apply']
|
||||
matchSorter(otherThings, 'app', {threshold: matchSorter.rankings.WORD_STARTS_WITH})
|
||||
// ['app', 'apple', 'apply', 'fiji apple'] (everything that matches with "word starts with" or better)
|
||||
```
|
||||
|
||||
### keepDiacritics: `boolean`
|
||||
|
||||
_Default: `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: true`
|
||||
|
||||
```javascript
|
||||
const thingsWithDiacritics = [
|
||||
'jalapeño',
|
||||
'à la carte',
|
||||
'café',
|
||||
'papier-mâché',
|
||||
'à la mode',
|
||||
]
|
||||
matchSorter(thingsWithDiacritics, 'aa')
|
||||
// ['jalapeño', 'à la carte', 'à la mode', 'papier-mâché']
|
||||
|
||||
matchSorter(thingsWithDiacritics, 'aa', {keepDiacritics: true})
|
||||
// ['jalapeño', 'à la carte']
|
||||
|
||||
matchSorter(thingsWithDiacritics, 'à', {keepDiacritics: true})
|
||||
// ['à la carte', 'à la mode']
|
||||
```
|
||||
|
||||
### baseSort: `function(itemA, itemB): -1 | 0 | 1`
|
||||
|
||||
_Default: `(a, b) => String(a.rankedValue).localeCompare(b.rankedValue)`_
|
||||
|
||||
By default, match-sorter uses the `String.localeCompare` function to tie-break
|
||||
items that have the same ranking. This results in a stable, alphabetic sort.
|
||||
|
||||
```javascript
|
||||
const list = ['C apple', 'B apple', 'A apple']
|
||||
matchSorter(list, 'apple')
|
||||
// ['A apple', 'B apple', 'C apple']
|
||||
```
|
||||
|
||||
_You can customize this behavior by specifying a custom `baseSort` function:_
|
||||
|
||||
```javascript
|
||||
const list = ['C apple', 'B apple', 'A apple']
|
||||
// This baseSort function will use the original index of items as the tie breaker
|
||||
matchSorter(list, 'apple', {baseSort: (a, b) => (a.index < b.index ? -1 : 1)})
|
||||
// ['C apple', 'B apple', 'A apple']
|
||||
```
|
||||
|
||||
### sorter: `function(rankedItems): rankedItems`
|
||||
|
||||
_Default:
|
||||
`matchedItems => matchedItems.sort((a, b) => sortRankedValues(a, b, baseSort))`_
|
||||
|
||||
By default, match-sorter uses an internal `sortRankedValues` function to sort
|
||||
items after matching them.
|
||||
|
||||
_You can customize the core sorting behavior by specifying a custom `sorter`
|
||||
function:_
|
||||
|
||||
Disable sorting entirely:
|
||||
```javascript
|
||||
const list = ['appl', 'C apple', 'B apple', 'A apple', 'app', 'applebutter']
|
||||
matchSorter(list, 'apple', {sorter: rankedItems => rankedItems})
|
||||
// ['C apple', 'B apple', 'A apple', 'applebutter']
|
||||
```
|
||||
|
||||
Return the unsorted rankedItems, but in reverse order:
|
||||
```javascript
|
||||
const list = ['appl', 'C apple', 'B apple', 'A apple', 'app', 'applebutter']
|
||||
matchSorter(list, 'apple', {sorter: rankedItems => [...rankedItems].reverse()})
|
||||
// ['applebutter', 'A apple', 'B apple', 'C apple']
|
||||
```
|
||||
|
||||
## Recipes
|
||||
|
||||
### Match PascalCase, camelCase, snake_case, or kebab-case as words
|
||||
|
||||
By default, `match-sorter` assumes spaces to be the word separator. However, if
|
||||
your data has a different word separator, you can use a property callback to
|
||||
replace your separator with spaces. For example, for `snake_case`:
|
||||
|
||||
```javascript
|
||||
const list = [
|
||||
{name: 'Janice_Kurtis'},
|
||||
{name: 'Fred_Mertz'},
|
||||
{name: 'George_Foreman'},
|
||||
{name: 'Jen_Smith'},
|
||||
]
|
||||
matchSorter(list, 'js', {keys: [item => item.name.replace(/_/g, ' ')]})
|
||||
// [{name: 'Jen_Smith'}, {name: 'Janice_Kurtis'}]
|
||||
```
|
||||
|
||||
### Match many words across multiple fields (table filtering)
|
||||
|
||||
By default, `match-sorter` will return matches from objects where one of the
|
||||
properties matches _the entire_ search term. For multi-column data sets it can
|
||||
be beneficial to split words in search string and match each word separately.
|
||||
This can be done by chaining `match-sorter` calls.
|
||||
|
||||
The benefit of this is that a filter string of "two words" will match both "two"
|
||||
and "words", but will return rows where the two words are found in _different_
|
||||
columns as well as when both words match in the same column. For single-column
|
||||
matches it will also return matches out of order (column = "wordstwo" will match
|
||||
just as well as column="twowords", the latter getting a higher score).
|
||||
|
||||
```javascript
|
||||
function fuzzySearchMultipleWords(
|
||||
rows, // array of data [{a: "a", b: "b"}, {a: "c", b: "d"}]
|
||||
keys, // keys to search ["a", "b"]
|
||||
filterValue: string, // potentially multi-word search string "two words"
|
||||
) {
|
||||
if (!filterValue || !filterValue.length) {
|
||||
return rows
|
||||
}
|
||||
|
||||
const terms = filterValue.split(' ')
|
||||
if (!terms) {
|
||||
return rows
|
||||
}
|
||||
|
||||
// reduceRight will mean sorting is done by score for the _first_ entered word.
|
||||
return terms.reduceRight(
|
||||
(results, term) => matchSorter(results, term, {keys}),
|
||||
rows,
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
[Multi-column code sandbox](https://codesandbox.io/s/match-sorter-example-forked-1ko35)
|
||||
|
||||
## Inspiration
|
||||
|
||||
Actually, most of this code was extracted from the _very first_ library I ever
|
||||
wrote: [genie][genie]!
|
||||
|
||||
## Other Solutions
|
||||
|
||||
You might try [Fuse.js](https://github.com/krisk/Fuse). It uses advanced math
|
||||
fanciness to get the closest match. Unfortunately what's "closest" doesn't
|
||||
always really make sense. So I extracted this from [genie][genie].
|
||||
|
||||
## Issues
|
||||
|
||||
_Looking to contribute? Look for the [Good First Issue][good-first-issue]
|
||||
label._
|
||||
|
||||
### 🐛 Bugs
|
||||
|
||||
Please file an issue for bugs, missing documentation, or unexpected behavior.
|
||||
|
||||
[**See Bugs**][bugs]
|
||||
|
||||
### 💡 Feature Requests
|
||||
|
||||
Please file an issue to suggest new features. Vote on feature requests by adding
|
||||
a 👍. This helps maintainers prioritize what to work on.
|
||||
|
||||
[**See Feature Requests**][requests]
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
Thanks goes to these people ([emoji key][emojis]):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://kentcdodds.com"><img src="https://avatars.githubusercontent.com/u/1500684?v=3?s=100" width="100px;" alt=""/><br /><sub><b>Kent C. Dodds</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=kentcdodds" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=kentcdodds" title="Documentation">📖</a> <a href="#infra-kentcdodds" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=kentcdodds" title="Tests">⚠️</a> <a href="https://github.com/kentcdodds/match-sorter/pulls?q=is%3Apr+reviewed-by%3Akentcdodds" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="http://conorhastings.com"><img src="https://avatars.githubusercontent.com/u/8263298?v=3?s=100" width="100px;" alt=""/><br /><sub><b>Conor Hastings</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=conorhastings" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=conorhastings" title="Documentation">📖</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=conorhastings" title="Tests">⚠️</a> <a href="https://github.com/kentcdodds/match-sorter/pulls?q=is%3Apr+reviewed-by%3Aconorhastings" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://github.com/rogeliog"><img src="https://avatars.githubusercontent.com/u/574806?v=3?s=100" width="100px;" alt=""/><br /><sub><b>Rogelio Guzman</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=rogeliog" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://ced.io"><img src="https://avatars.githubusercontent.com/u/1416436?v=3?s=100" width="100px;" alt=""/><br /><sub><b>Claudéric Demers</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=clauderic" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=clauderic" title="Documentation">📖</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=clauderic" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="kevindav.us"><img src="https://avatars3.githubusercontent.com/u/4150097?v=3?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Davis</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=osfan501" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=osfan501" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/nfdjps"><img src="https://avatars1.githubusercontent.com/u/19157735?v=3?s=100" width="100px;" alt=""/><br /><sub><b>Denver Chen</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=nfdjps" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=nfdjps" title="Documentation">📖</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=nfdjps" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="http://ruigrok.info"><img src="https://avatars0.githubusercontent.com/u/12719057?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian Ruigrok</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/issues?q=author%3AChrisRu" title="Bug reports">🐛</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=ChrisRu" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=ChrisRu" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/hozefaj"><img src="https://avatars1.githubusercontent.com/u/2084833?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hozefa</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/issues?q=author%3Ahozefaj" title="Bug reports">🐛</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=hozefaj" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=hozefaj" title="Tests">⚠️</a> <a href="#ideas-hozefaj" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/pushpinder107"><img src="https://avatars3.githubusercontent.com/u/9403361?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pushpinder107</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=pushpinder107" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/tikotzky"><img src="https://avatars3.githubusercontent.com/u/200528?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mordy Tikotzky</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=tikotzky" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=tikotzky" title="Documentation">📖</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=tikotzky" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/sdbrannum"><img src="https://avatars1.githubusercontent.com/u/11765845?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Steven Brannum</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=sdbrannum" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=sdbrannum" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/cmeeren"><img src="https://avatars0.githubusercontent.com/u/7766733?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christer van der Meeren</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/issues?q=author%3Acmeeren" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="http://securitynull.net/"><img src="https://avatars0.githubusercontent.com/u/3801362?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Samuel Petrosyan</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=samyan" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/issues?q=author%3Asamyan" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://brandonkalinowski.com"><img src="https://avatars3.githubusercontent.com/u/4714862?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brandon Kalinowski</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/issues?q=author%3Abrandonkal" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://codefund.io"><img src="https://avatars2.githubusercontent.com/u/12481?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eric Berry</b></sub></a><br /><a href="#fundingFinding-coderberry" title="Funding Finding">🔍</a></td>
|
||||
<td align="center"><a href="https://github.com/skube"><img src="https://avatars3.githubusercontent.com/u/146396?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Skubie Doo</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=skube" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://michaeldeboey.be"><img src="https://avatars3.githubusercontent.com/u/6643991?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michaël De Boey</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=MichaelDeBoey" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/pulls?q=is%3Apr+reviewed-by%3AMichaelDeBoey" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://tannerlinsley.com"><img src="https://avatars0.githubusercontent.com/u/5580297?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tanner Linsley</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=tannerlinsley" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=tannerlinsley" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/SweVictor"><img src="https://avatars1.githubusercontent.com/u/449347?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Victor</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=SweVictor" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/RebeccaStevens"><img src="https://avatars1.githubusercontent.com/u/7224206?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rebecca Stevens</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/issues?q=author%3ARebeccaStevens" title="Bug reports">🐛</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=RebeccaStevens" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/marcosvega91"><img src="https://avatars2.githubusercontent.com/u/5365582?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marco Moretti</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=marcosvega91" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://ricardobusquet.com"><img src="https://avatars1.githubusercontent.com/u/7198302?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricardo Busquet</b></sub></a><br /><a href="#ideas-rbusquet" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/kentcdodds/match-sorter/pulls?q=is%3Apr+reviewed-by%3Arbusquet" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=rbusquet" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/weyert"><img src="https://avatars3.githubusercontent.com/u/7049?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Weyert de Boer</b></sub></a><br /><a href="#ideas-weyert" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/kentcdodds/match-sorter/pulls?q=is%3Apr+reviewed-by%3Aweyert" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://github.com/PhilGarb"><img src="https://avatars3.githubusercontent.com/u/38015558?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Philipp Garbowsky</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=PhilGarb" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/mart-jansink"><img src="https://avatars3.githubusercontent.com/u/4381258?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mart</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=mart-jansink" title="Code">💻</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=mart-jansink" title="Tests">⚠️</a> <a href="https://github.com/kentcdodds/match-sorter/commits?author=mart-jansink" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/levenleven"><img src="https://avatars.githubusercontent.com/u/6463364?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksey Levenstein</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=levenleven" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/diesieben07"><img src="https://avatars.githubusercontent.com/u/1915984?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Take Weiland</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=diesieben07" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/AmitAber"><img src="https://avatars.githubusercontent.com/u/8988867?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amit Abershitz</b></sub></a><br /><a href="https://github.com/kentcdodds/match-sorter/commits?author=AmitAber" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors][all-contributors] specification.
|
||||
Contributions of any kind welcome!
|
||||
|
||||
## LICENSE
|
||||
|
||||
MIT
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[npm]: https://www.npmjs.com
|
||||
[node]: https://nodejs.org
|
||||
[build-badge]: https://img.shields.io/github/workflow/status/kentcdodds/match-sorter/validate?logo=github&style=flat-square
|
||||
[build]: https://github.com/kentcdodds/match-sorter/actions?query=workflow%3Avalidate
|
||||
[coverage-badge]: https://img.shields.io/codecov/c/github/kentcdodds/match-sorter.svg?style=flat-square
|
||||
[coverage]: https://codecov.io/github/kentcdodds/match-sorter
|
||||
[version-badge]: https://img.shields.io/npm/v/match-sorter.svg?style=flat-square
|
||||
[package]: https://www.npmjs.com/package/match-sorter
|
||||
[downloads-badge]: https://img.shields.io/npm/dm/match-sorter.svg?style=flat-square
|
||||
[npmtrends]: https://www.npmtrends.com/match-sorter
|
||||
[license-badge]: https://img.shields.io/npm/l/match-sorter.svg?style=flat-square
|
||||
[license]: https://github.com/kentcdodds/match-sorter/blob/master/LICENSE
|
||||
[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
|
||||
[prs]: http://makeapullrequest.com
|
||||
[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
|
||||
[coc]: https://github.com/kentcdodds/match-sorter/blob/master/CODE_OF_CONDUCT.md
|
||||
[examples-badge]: https://img.shields.io/badge/%F0%9F%92%A1-examples-8C8E93.svg?style=flat-square
|
||||
[examples]: https://github.com/kentcdodds/match-sorter/blob/master/other/EXAMPLES.md
|
||||
[emojis]: https://github.com/all-contributors/all-contributors#emoji-key
|
||||
[all-contributors]: https://github.com/all-contributors/all-contributors
|
||||
[all-contributors-badge]: https://img.shields.io/github/all-contributors/kentcdodds/match-sorter?color=orange&style=flat-square
|
||||
[bugs]: https://github.com/kentcdodds/match-sorter/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Acreated-desc+label%3Abug
|
||||
[requests]: https://github.com/kentcdodds/match-sorter/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement
|
||||
[good-first-issue]: https://github.com/kentcdodds/match-sorter/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement+label%3A%22good+first+issue%22
|
||||
|
||||
[genie]: https://github.com/kentcdodds/genie
|
||||
<!-- prettier-ignore-end -->
|
||||
+1
@@ -0,0 +1 @@
|
||||
export {};
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
type KeyAttributes = {
|
||||
threshold?: Ranking;
|
||||
maxRanking: Ranking;
|
||||
minRanking: Ranking;
|
||||
};
|
||||
interface RankingInfo {
|
||||
rankedValue: string;
|
||||
rank: Ranking;
|
||||
keyIndex: number;
|
||||
keyThreshold: Ranking | undefined;
|
||||
}
|
||||
interface ValueGetterKey<ItemType> {
|
||||
(item: ItemType): string | Array<string>;
|
||||
}
|
||||
interface IndexedItem<ItemType> {
|
||||
item: ItemType;
|
||||
index: number;
|
||||
}
|
||||
interface RankedItem<ItemType> extends RankingInfo, IndexedItem<ItemType> {
|
||||
}
|
||||
interface BaseSorter<ItemType> {
|
||||
(a: RankedItem<ItemType>, b: RankedItem<ItemType>): number;
|
||||
}
|
||||
interface Sorter<ItemType> {
|
||||
(matchItems: Array<RankedItem<ItemType>>): Array<RankedItem<ItemType>>;
|
||||
}
|
||||
interface KeyAttributesOptions<ItemType> {
|
||||
key?: string | ValueGetterKey<ItemType>;
|
||||
threshold?: Ranking;
|
||||
maxRanking?: Ranking;
|
||||
minRanking?: Ranking;
|
||||
}
|
||||
type KeyOption<ItemType> = KeyAttributesOptions<ItemType> | ValueGetterKey<ItemType> | string;
|
||||
interface MatchSorterOptions<ItemType = unknown> {
|
||||
keys?: ReadonlyArray<KeyOption<ItemType>>;
|
||||
threshold?: Ranking;
|
||||
baseSort?: BaseSorter<ItemType>;
|
||||
keepDiacritics?: boolean;
|
||||
sorter?: Sorter<ItemType>;
|
||||
}
|
||||
declare const rankings: {
|
||||
readonly CASE_SENSITIVE_EQUAL: 7;
|
||||
readonly EQUAL: 6;
|
||||
readonly STARTS_WITH: 5;
|
||||
readonly WORD_STARTS_WITH: 4;
|
||||
readonly CONTAINS: 3;
|
||||
readonly ACRONYM: 2;
|
||||
readonly MATCHES: 1;
|
||||
readonly NO_MATCH: 0;
|
||||
};
|
||||
type Ranking = typeof rankings[keyof typeof rankings];
|
||||
declare const defaultBaseSortFn: BaseSorter<unknown>;
|
||||
/**
|
||||
* Takes an array of items and a value and returns a new array with the items that match the given value
|
||||
* @param {Array} items - the items to sort
|
||||
* @param {String} value - the value to use for ranking
|
||||
* @param {Object} options - Some options to configure the sorter
|
||||
* @return {Array} - the new sorted array
|
||||
*/
|
||||
declare function matchSorter<ItemType = string>(items: ReadonlyArray<ItemType>, value: string, options?: MatchSorterOptions<ItemType>): Array<ItemType>;
|
||||
declare namespace matchSorter {
|
||||
var rankings: {
|
||||
readonly CASE_SENSITIVE_EQUAL: 7;
|
||||
readonly EQUAL: 6;
|
||||
readonly STARTS_WITH: 5;
|
||||
readonly WORD_STARTS_WITH: 4;
|
||||
readonly CONTAINS: 3;
|
||||
readonly ACRONYM: 2;
|
||||
readonly MATCHES: 1;
|
||||
readonly NO_MATCH: 0;
|
||||
};
|
||||
}
|
||||
export { matchSorter, rankings, defaultBaseSortFn };
|
||||
export type { MatchSorterOptions, KeyAttributesOptions, KeyOption, KeyAttributes, RankingInfo, ValueGetterKey, };
|
||||
+428
@@ -0,0 +1,428 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var removeAccents = require('remove-accents');
|
||||
|
||||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
||||
|
||||
var removeAccents__default = /*#__PURE__*/_interopDefaultLegacy(removeAccents);
|
||||
|
||||
/**
|
||||
* @name match-sorter
|
||||
* @license MIT license.
|
||||
* @copyright (c) 2020 Kent C. Dodds
|
||||
* @author Kent C. Dodds <me@kentcdodds.com> (https://kentcdodds.com)
|
||||
*/
|
||||
const rankings = {
|
||||
CASE_SENSITIVE_EQUAL: 7,
|
||||
EQUAL: 6,
|
||||
STARTS_WITH: 5,
|
||||
WORD_STARTS_WITH: 4,
|
||||
CONTAINS: 3,
|
||||
ACRONYM: 2,
|
||||
MATCHES: 1,
|
||||
NO_MATCH: 0
|
||||
};
|
||||
const defaultBaseSortFn = (a, b) => String(a.rankedValue).localeCompare(String(b.rankedValue));
|
||||
|
||||
/**
|
||||
* Takes an array of items and a value and returns a new array with the items that match the given value
|
||||
* @param {Array} items - the items to sort
|
||||
* @param {String} value - the value to use for ranking
|
||||
* @param {Object} options - Some options to configure the sorter
|
||||
* @return {Array} - the new sorted array
|
||||
*/
|
||||
function matchSorter(items, value, options) {
|
||||
if (options === void 0) {
|
||||
options = {};
|
||||
}
|
||||
const {
|
||||
keys,
|
||||
threshold = rankings.MATCHES,
|
||||
baseSort = defaultBaseSortFn,
|
||||
sorter = matchedItems => matchedItems.sort((a, b) => sortRankedValues(a, b, baseSort))
|
||||
} = options;
|
||||
const matchedItems = items.reduce(reduceItemsToRanked, []);
|
||||
return sorter(matchedItems).map(_ref => {
|
||||
let {
|
||||
item
|
||||
} = _ref;
|
||||
return item;
|
||||
});
|
||||
function reduceItemsToRanked(matches, item, index) {
|
||||
const rankingInfo = getHighestRanking(item, keys, value, options);
|
||||
const {
|
||||
rank,
|
||||
keyThreshold = threshold
|
||||
} = rankingInfo;
|
||||
if (rank >= keyThreshold) {
|
||||
matches.push({
|
||||
...rankingInfo,
|
||||
item,
|
||||
index
|
||||
});
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
matchSorter.rankings = rankings;
|
||||
|
||||
/**
|
||||
* 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 {{rank: Number, keyIndex: Number, keyThreshold: Number}} - the highest ranking
|
||||
*/
|
||||
function getHighestRanking(item, keys, value, options) {
|
||||
if (!keys) {
|
||||
// if keys is not specified, then we assume the item given is ready to be matched
|
||||
const stringItem = item;
|
||||
return {
|
||||
// ends up being duplicate of 'item' in matches but consistent
|
||||
rankedValue: stringItem,
|
||||
rank: getMatchRanking(stringItem, value, options),
|
||||
keyIndex: -1,
|
||||
keyThreshold: options.threshold
|
||||
};
|
||||
}
|
||||
const valuesToRank = getAllValuesToRank(item, keys);
|
||||
return valuesToRank.reduce((_ref2, _ref3, i) => {
|
||||
let {
|
||||
rank,
|
||||
rankedValue,
|
||||
keyIndex,
|
||||
keyThreshold
|
||||
} = _ref2;
|
||||
let {
|
||||
itemValue,
|
||||
attributes
|
||||
} = _ref3;
|
||||
let newRank = getMatchRanking(itemValue, value, options);
|
||||
let newRankedValue = rankedValue;
|
||||
const {
|
||||
minRanking,
|
||||
maxRanking,
|
||||
threshold
|
||||
} = attributes;
|
||||
if (newRank < minRanking && newRank >= rankings.MATCHES) {
|
||||
newRank = minRanking;
|
||||
} else if (newRank > maxRanking) {
|
||||
newRank = maxRanking;
|
||||
}
|
||||
if (newRank > rank) {
|
||||
rank = newRank;
|
||||
keyIndex = i;
|
||||
keyThreshold = threshold;
|
||||
newRankedValue = itemValue;
|
||||
}
|
||||
return {
|
||||
rankedValue: newRankedValue,
|
||||
rank,
|
||||
keyIndex,
|
||||
keyThreshold
|
||||
};
|
||||
}, {
|
||||
rankedValue: item,
|
||||
rank: rankings.NO_MATCH,
|
||||
keyIndex: -1,
|
||||
keyThreshold: options.threshold
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, options) {
|
||||
testString = prepareValueForComparison(testString, options);
|
||||
stringToRank = prepareValueForComparison(stringToRank, options);
|
||||
|
||||
// too long
|
||||
if (stringToRank.length > testString.length) {
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
|
||||
// case sensitive equals
|
||||
if (testString === stringToRank) {
|
||||
return rankings.CASE_SENSITIVE_EQUAL;
|
||||
}
|
||||
|
||||
// Lower casing before further comparison
|
||||
testString = testString.toLowerCase();
|
||||
stringToRank = stringToRank.toLowerCase();
|
||||
|
||||
// case insensitive equals
|
||||
if (testString === stringToRank) {
|
||||
return rankings.EQUAL;
|
||||
}
|
||||
|
||||
// starts with
|
||||
if (testString.startsWith(stringToRank)) {
|
||||
return rankings.STARTS_WITH;
|
||||
}
|
||||
|
||||
// word starts with
|
||||
if (testString.includes(` ${stringToRank}`)) {
|
||||
return rankings.WORD_STARTS_WITH;
|
||||
}
|
||||
|
||||
// contains
|
||||
if (testString.includes(stringToRank)) {
|
||||
return rankings.CONTAINS;
|
||||
} else if (stringToRank.length === 1) {
|
||||
// If the only character in the given stringToRank
|
||||
// isn't even contained in the testString, then
|
||||
// it's definitely not a match.
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
|
||||
// acronym
|
||||
if (getAcronym(testString).includes(stringToRank)) {
|
||||
return rankings.ACRONYM;
|
||||
}
|
||||
|
||||
// will return a number between rankings.MATCHES and
|
||||
// rankings.MATCHES + 1 depending on how close of a match it is.
|
||||
return getClosenessRanking(testString, stringToRank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an acronym for a string.
|
||||
*
|
||||
* @param {String} string the string for which to produce the acronym
|
||||
* @returns {String} the acronym
|
||||
*/
|
||||
function getAcronym(string) {
|
||||
let acronym = '';
|
||||
const wordsInString = string.split(' ');
|
||||
wordsInString.forEach(wordInString => {
|
||||
const splitByHyphenWords = wordInString.split('-');
|
||||
splitByHyphenWords.forEach(splitByHyphenWord => {
|
||||
acronym += splitByHyphenWord.substr(0, 1);
|
||||
});
|
||||
});
|
||||
return acronym;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a score based on how spread apart the
|
||||
* characters from the stringToRank are within the testString.
|
||||
* A number close to rankings.MATCHES represents a loose match. A number close
|
||||
* to rankings.MATCHES + 1 represents a tighter match.
|
||||
* @param {String} testString - the string to test against
|
||||
* @param {String} stringToRank - the string to rank
|
||||
* @returns {Number} the number between rankings.MATCHES and
|
||||
* rankings.MATCHES + 1 for how well stringToRank matches testString
|
||||
*/
|
||||
function getClosenessRanking(testString, stringToRank) {
|
||||
let matchingInOrderCharCount = 0;
|
||||
let charNumber = 0;
|
||||
function findMatchingCharacter(matchChar, string, index) {
|
||||
for (let j = index, J = string.length; j < J; j++) {
|
||||
const stringChar = string[j];
|
||||
if (stringChar === matchChar) {
|
||||
matchingInOrderCharCount += 1;
|
||||
return j + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
function getRanking(spread) {
|
||||
const spreadPercentage = 1 / spread;
|
||||
const inOrderPercentage = matchingInOrderCharCount / stringToRank.length;
|
||||
const ranking = rankings.MATCHES + inOrderPercentage * spreadPercentage;
|
||||
return ranking;
|
||||
}
|
||||
const firstIndex = findMatchingCharacter(stringToRank[0], testString, 0);
|
||||
if (firstIndex < 0) {
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
charNumber = firstIndex;
|
||||
for (let i = 1, I = stringToRank.length; i < I; i++) {
|
||||
const matchChar = stringToRank[i];
|
||||
charNumber = findMatchingCharacter(matchChar, testString, charNumber);
|
||||
const found = charNumber > -1;
|
||||
if (!found) {
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
}
|
||||
const spread = charNumber - firstIndex;
|
||||
return getRanking(spread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts items that have a rank, index, and keyIndex
|
||||
* @param {Object} a - the first item to sort
|
||||
* @param {Object} b - the second item to sort
|
||||
* @return {Number} -1 if a should come first, 1 if b should come first, 0 if equal
|
||||
*/
|
||||
function sortRankedValues(a, b, baseSort) {
|
||||
const aFirst = -1;
|
||||
const bFirst = 1;
|
||||
const {
|
||||
rank: aRank,
|
||||
keyIndex: aKeyIndex
|
||||
} = a;
|
||||
const {
|
||||
rank: bRank,
|
||||
keyIndex: bKeyIndex
|
||||
} = b;
|
||||
const same = aRank === bRank;
|
||||
if (same) {
|
||||
if (aKeyIndex === bKeyIndex) {
|
||||
// use the base sort function as a tie-breaker
|
||||
return baseSort(a, b);
|
||||
} else {
|
||||
return aKeyIndex < bKeyIndex ? aFirst : bFirst;
|
||||
}
|
||||
} else {
|
||||
return aRank > bRank ? aFirst : bFirst;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares value for comparison by stringifying it, removing diacritics (if specified)
|
||||
* @param {String} value - the value to clean
|
||||
* @param {Object} options - {keepDiacritics: whether to remove diacritics}
|
||||
* @return {String} the prepared value
|
||||
*/
|
||||
function prepareValueForComparison(value, _ref4) {
|
||||
let {
|
||||
keepDiacritics
|
||||
} = _ref4;
|
||||
// value might not actually be a string at this point (we don't get to choose)
|
||||
// so part of preparing the value for comparison is ensure that it is a string
|
||||
value = `${value}`; // toString
|
||||
if (!keepDiacritics) {
|
||||
value = removeAccents__default["default"](value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets value for key in item at arbitrarily nested keypath
|
||||
* @param {Object} item - the item
|
||||
* @param {Object|Function} key - the potentially nested keypath or property callback
|
||||
* @return {Array} - an array containing the value(s) at the nested keypath
|
||||
*/
|
||||
function getItemValues(item, key) {
|
||||
if (typeof key === 'object') {
|
||||
key = key.key;
|
||||
}
|
||||
let value;
|
||||
if (typeof key === 'function') {
|
||||
value = key(item);
|
||||
} else if (item == null) {
|
||||
value = null;
|
||||
} else if (Object.hasOwnProperty.call(item, key)) {
|
||||
value = item[key];
|
||||
} else if (key.includes('.')) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
return getNestedValues(key, item);
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
|
||||
// because `value` can also be undefined
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
return [String(value)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Given path: "foo.bar.baz"
|
||||
* And item: {foo: {bar: {baz: 'buzz'}}}
|
||||
* -> 'buzz'
|
||||
* @param path a dot-separated set of keys
|
||||
* @param item the item to get the value from
|
||||
*/
|
||||
function getNestedValues(path, item) {
|
||||
const keys = path.split('.');
|
||||
let values = [item];
|
||||
for (let i = 0, I = keys.length; i < I; i++) {
|
||||
const nestedKey = keys[i];
|
||||
let nestedValues = [];
|
||||
for (let j = 0, J = values.length; j < J; j++) {
|
||||
const nestedItem = values[j];
|
||||
if (nestedItem == null) continue;
|
||||
if (Object.hasOwnProperty.call(nestedItem, nestedKey)) {
|
||||
const nestedValue = nestedItem[nestedKey];
|
||||
if (nestedValue != null) {
|
||||
nestedValues.push(nestedValue);
|
||||
}
|
||||
} else if (nestedKey === '*') {
|
||||
// ensure that values is an array
|
||||
nestedValues = nestedValues.concat(nestedItem);
|
||||
}
|
||||
}
|
||||
values = nestedValues;
|
||||
}
|
||||
if (Array.isArray(values[0])) {
|
||||
// keep allowing the implicit wildcard for an array of strings at the end of
|
||||
// the path; don't use `.flat()` because that's not available in node.js v10
|
||||
const result = [];
|
||||
return result.concat(...values);
|
||||
}
|
||||
// Based on our logic it should be an array of strings by now...
|
||||
// assuming the user's path terminated in strings
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the values for the given keys in the given item and returns an array of those values
|
||||
* @param item - the item from which the values will be retrieved
|
||||
* @param keys - the keys to use to retrieve the values
|
||||
* @return objects with {itemValue, attributes}
|
||||
*/
|
||||
function getAllValuesToRank(item, keys) {
|
||||
const allValues = [];
|
||||
for (let j = 0, J = keys.length; j < J; j++) {
|
||||
const key = keys[j];
|
||||
const attributes = getKeyAttributes(key);
|
||||
const itemValues = getItemValues(item, key);
|
||||
for (let i = 0, I = itemValues.length; i < I; i++) {
|
||||
allValues.push({
|
||||
itemValue: itemValues[i],
|
||||
attributes
|
||||
});
|
||||
}
|
||||
}
|
||||
return allValues;
|
||||
}
|
||||
const defaultKeyAttributes = {
|
||||
maxRanking: Infinity,
|
||||
minRanking: -Infinity
|
||||
};
|
||||
/**
|
||||
* Gets all the attributes for the given key
|
||||
* @param key - the key from which the attributes will be retrieved
|
||||
* @return object containing the key's attributes
|
||||
*/
|
||||
function getKeyAttributes(key) {
|
||||
if (typeof key === 'string') {
|
||||
return defaultKeyAttributes;
|
||||
}
|
||||
return {
|
||||
...defaultKeyAttributes,
|
||||
...key
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
eslint
|
||||
no-continue: "off",
|
||||
*/
|
||||
|
||||
exports.defaultBaseSortFn = defaultBaseSortFn;
|
||||
exports.matchSorter = matchSorter;
|
||||
exports.rankings = rankings;
|
||||
+418
@@ -0,0 +1,418 @@
|
||||
import removeAccents from 'remove-accents';
|
||||
|
||||
/**
|
||||
* @name match-sorter
|
||||
* @license MIT license.
|
||||
* @copyright (c) 2020 Kent C. Dodds
|
||||
* @author Kent C. Dodds <me@kentcdodds.com> (https://kentcdodds.com)
|
||||
*/
|
||||
const rankings = {
|
||||
CASE_SENSITIVE_EQUAL: 7,
|
||||
EQUAL: 6,
|
||||
STARTS_WITH: 5,
|
||||
WORD_STARTS_WITH: 4,
|
||||
CONTAINS: 3,
|
||||
ACRONYM: 2,
|
||||
MATCHES: 1,
|
||||
NO_MATCH: 0
|
||||
};
|
||||
const defaultBaseSortFn = (a, b) => String(a.rankedValue).localeCompare(String(b.rankedValue));
|
||||
|
||||
/**
|
||||
* Takes an array of items and a value and returns a new array with the items that match the given value
|
||||
* @param {Array} items - the items to sort
|
||||
* @param {String} value - the value to use for ranking
|
||||
* @param {Object} options - Some options to configure the sorter
|
||||
* @return {Array} - the new sorted array
|
||||
*/
|
||||
function matchSorter(items, value, options) {
|
||||
if (options === void 0) {
|
||||
options = {};
|
||||
}
|
||||
const {
|
||||
keys,
|
||||
threshold = rankings.MATCHES,
|
||||
baseSort = defaultBaseSortFn,
|
||||
sorter = matchedItems => matchedItems.sort((a, b) => sortRankedValues(a, b, baseSort))
|
||||
} = options;
|
||||
const matchedItems = items.reduce(reduceItemsToRanked, []);
|
||||
return sorter(matchedItems).map(_ref => {
|
||||
let {
|
||||
item
|
||||
} = _ref;
|
||||
return item;
|
||||
});
|
||||
function reduceItemsToRanked(matches, item, index) {
|
||||
const rankingInfo = getHighestRanking(item, keys, value, options);
|
||||
const {
|
||||
rank,
|
||||
keyThreshold = threshold
|
||||
} = rankingInfo;
|
||||
if (rank >= keyThreshold) {
|
||||
matches.push({
|
||||
...rankingInfo,
|
||||
item,
|
||||
index
|
||||
});
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
matchSorter.rankings = rankings;
|
||||
|
||||
/**
|
||||
* 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 {{rank: Number, keyIndex: Number, keyThreshold: Number}} - the highest ranking
|
||||
*/
|
||||
function getHighestRanking(item, keys, value, options) {
|
||||
if (!keys) {
|
||||
// if keys is not specified, then we assume the item given is ready to be matched
|
||||
const stringItem = item;
|
||||
return {
|
||||
// ends up being duplicate of 'item' in matches but consistent
|
||||
rankedValue: stringItem,
|
||||
rank: getMatchRanking(stringItem, value, options),
|
||||
keyIndex: -1,
|
||||
keyThreshold: options.threshold
|
||||
};
|
||||
}
|
||||
const valuesToRank = getAllValuesToRank(item, keys);
|
||||
return valuesToRank.reduce((_ref2, _ref3, i) => {
|
||||
let {
|
||||
rank,
|
||||
rankedValue,
|
||||
keyIndex,
|
||||
keyThreshold
|
||||
} = _ref2;
|
||||
let {
|
||||
itemValue,
|
||||
attributes
|
||||
} = _ref3;
|
||||
let newRank = getMatchRanking(itemValue, value, options);
|
||||
let newRankedValue = rankedValue;
|
||||
const {
|
||||
minRanking,
|
||||
maxRanking,
|
||||
threshold
|
||||
} = attributes;
|
||||
if (newRank < minRanking && newRank >= rankings.MATCHES) {
|
||||
newRank = minRanking;
|
||||
} else if (newRank > maxRanking) {
|
||||
newRank = maxRanking;
|
||||
}
|
||||
if (newRank > rank) {
|
||||
rank = newRank;
|
||||
keyIndex = i;
|
||||
keyThreshold = threshold;
|
||||
newRankedValue = itemValue;
|
||||
}
|
||||
return {
|
||||
rankedValue: newRankedValue,
|
||||
rank,
|
||||
keyIndex,
|
||||
keyThreshold
|
||||
};
|
||||
}, {
|
||||
rankedValue: item,
|
||||
rank: rankings.NO_MATCH,
|
||||
keyIndex: -1,
|
||||
keyThreshold: options.threshold
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, options) {
|
||||
testString = prepareValueForComparison(testString, options);
|
||||
stringToRank = prepareValueForComparison(stringToRank, options);
|
||||
|
||||
// too long
|
||||
if (stringToRank.length > testString.length) {
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
|
||||
// case sensitive equals
|
||||
if (testString === stringToRank) {
|
||||
return rankings.CASE_SENSITIVE_EQUAL;
|
||||
}
|
||||
|
||||
// Lower casing before further comparison
|
||||
testString = testString.toLowerCase();
|
||||
stringToRank = stringToRank.toLowerCase();
|
||||
|
||||
// case insensitive equals
|
||||
if (testString === stringToRank) {
|
||||
return rankings.EQUAL;
|
||||
}
|
||||
|
||||
// starts with
|
||||
if (testString.startsWith(stringToRank)) {
|
||||
return rankings.STARTS_WITH;
|
||||
}
|
||||
|
||||
// word starts with
|
||||
if (testString.includes(` ${stringToRank}`)) {
|
||||
return rankings.WORD_STARTS_WITH;
|
||||
}
|
||||
|
||||
// contains
|
||||
if (testString.includes(stringToRank)) {
|
||||
return rankings.CONTAINS;
|
||||
} else if (stringToRank.length === 1) {
|
||||
// If the only character in the given stringToRank
|
||||
// isn't even contained in the testString, then
|
||||
// it's definitely not a match.
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
|
||||
// acronym
|
||||
if (getAcronym(testString).includes(stringToRank)) {
|
||||
return rankings.ACRONYM;
|
||||
}
|
||||
|
||||
// will return a number between rankings.MATCHES and
|
||||
// rankings.MATCHES + 1 depending on how close of a match it is.
|
||||
return getClosenessRanking(testString, stringToRank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an acronym for a string.
|
||||
*
|
||||
* @param {String} string the string for which to produce the acronym
|
||||
* @returns {String} the acronym
|
||||
*/
|
||||
function getAcronym(string) {
|
||||
let acronym = '';
|
||||
const wordsInString = string.split(' ');
|
||||
wordsInString.forEach(wordInString => {
|
||||
const splitByHyphenWords = wordInString.split('-');
|
||||
splitByHyphenWords.forEach(splitByHyphenWord => {
|
||||
acronym += splitByHyphenWord.substr(0, 1);
|
||||
});
|
||||
});
|
||||
return acronym;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a score based on how spread apart the
|
||||
* characters from the stringToRank are within the testString.
|
||||
* A number close to rankings.MATCHES represents a loose match. A number close
|
||||
* to rankings.MATCHES + 1 represents a tighter match.
|
||||
* @param {String} testString - the string to test against
|
||||
* @param {String} stringToRank - the string to rank
|
||||
* @returns {Number} the number between rankings.MATCHES and
|
||||
* rankings.MATCHES + 1 for how well stringToRank matches testString
|
||||
*/
|
||||
function getClosenessRanking(testString, stringToRank) {
|
||||
let matchingInOrderCharCount = 0;
|
||||
let charNumber = 0;
|
||||
function findMatchingCharacter(matchChar, string, index) {
|
||||
for (let j = index, J = string.length; j < J; j++) {
|
||||
const stringChar = string[j];
|
||||
if (stringChar === matchChar) {
|
||||
matchingInOrderCharCount += 1;
|
||||
return j + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
function getRanking(spread) {
|
||||
const spreadPercentage = 1 / spread;
|
||||
const inOrderPercentage = matchingInOrderCharCount / stringToRank.length;
|
||||
const ranking = rankings.MATCHES + inOrderPercentage * spreadPercentage;
|
||||
return ranking;
|
||||
}
|
||||
const firstIndex = findMatchingCharacter(stringToRank[0], testString, 0);
|
||||
if (firstIndex < 0) {
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
charNumber = firstIndex;
|
||||
for (let i = 1, I = stringToRank.length; i < I; i++) {
|
||||
const matchChar = stringToRank[i];
|
||||
charNumber = findMatchingCharacter(matchChar, testString, charNumber);
|
||||
const found = charNumber > -1;
|
||||
if (!found) {
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
}
|
||||
const spread = charNumber - firstIndex;
|
||||
return getRanking(spread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts items that have a rank, index, and keyIndex
|
||||
* @param {Object} a - the first item to sort
|
||||
* @param {Object} b - the second item to sort
|
||||
* @return {Number} -1 if a should come first, 1 if b should come first, 0 if equal
|
||||
*/
|
||||
function sortRankedValues(a, b, baseSort) {
|
||||
const aFirst = -1;
|
||||
const bFirst = 1;
|
||||
const {
|
||||
rank: aRank,
|
||||
keyIndex: aKeyIndex
|
||||
} = a;
|
||||
const {
|
||||
rank: bRank,
|
||||
keyIndex: bKeyIndex
|
||||
} = b;
|
||||
const same = aRank === bRank;
|
||||
if (same) {
|
||||
if (aKeyIndex === bKeyIndex) {
|
||||
// use the base sort function as a tie-breaker
|
||||
return baseSort(a, b);
|
||||
} else {
|
||||
return aKeyIndex < bKeyIndex ? aFirst : bFirst;
|
||||
}
|
||||
} else {
|
||||
return aRank > bRank ? aFirst : bFirst;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares value for comparison by stringifying it, removing diacritics (if specified)
|
||||
* @param {String} value - the value to clean
|
||||
* @param {Object} options - {keepDiacritics: whether to remove diacritics}
|
||||
* @return {String} the prepared value
|
||||
*/
|
||||
function prepareValueForComparison(value, _ref4) {
|
||||
let {
|
||||
keepDiacritics
|
||||
} = _ref4;
|
||||
// value might not actually be a string at this point (we don't get to choose)
|
||||
// so part of preparing the value for comparison is ensure that it is a string
|
||||
value = `${value}`; // toString
|
||||
if (!keepDiacritics) {
|
||||
value = removeAccents(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets value for key in item at arbitrarily nested keypath
|
||||
* @param {Object} item - the item
|
||||
* @param {Object|Function} key - the potentially nested keypath or property callback
|
||||
* @return {Array} - an array containing the value(s) at the nested keypath
|
||||
*/
|
||||
function getItemValues(item, key) {
|
||||
if (typeof key === 'object') {
|
||||
key = key.key;
|
||||
}
|
||||
let value;
|
||||
if (typeof key === 'function') {
|
||||
value = key(item);
|
||||
} else if (item == null) {
|
||||
value = null;
|
||||
} else if (Object.hasOwnProperty.call(item, key)) {
|
||||
value = item[key];
|
||||
} else if (key.includes('.')) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
return getNestedValues(key, item);
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
|
||||
// because `value` can also be undefined
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
return [String(value)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Given path: "foo.bar.baz"
|
||||
* And item: {foo: {bar: {baz: 'buzz'}}}
|
||||
* -> 'buzz'
|
||||
* @param path a dot-separated set of keys
|
||||
* @param item the item to get the value from
|
||||
*/
|
||||
function getNestedValues(path, item) {
|
||||
const keys = path.split('.');
|
||||
let values = [item];
|
||||
for (let i = 0, I = keys.length; i < I; i++) {
|
||||
const nestedKey = keys[i];
|
||||
let nestedValues = [];
|
||||
for (let j = 0, J = values.length; j < J; j++) {
|
||||
const nestedItem = values[j];
|
||||
if (nestedItem == null) continue;
|
||||
if (Object.hasOwnProperty.call(nestedItem, nestedKey)) {
|
||||
const nestedValue = nestedItem[nestedKey];
|
||||
if (nestedValue != null) {
|
||||
nestedValues.push(nestedValue);
|
||||
}
|
||||
} else if (nestedKey === '*') {
|
||||
// ensure that values is an array
|
||||
nestedValues = nestedValues.concat(nestedItem);
|
||||
}
|
||||
}
|
||||
values = nestedValues;
|
||||
}
|
||||
if (Array.isArray(values[0])) {
|
||||
// keep allowing the implicit wildcard for an array of strings at the end of
|
||||
// the path; don't use `.flat()` because that's not available in node.js v10
|
||||
const result = [];
|
||||
return result.concat(...values);
|
||||
}
|
||||
// Based on our logic it should be an array of strings by now...
|
||||
// assuming the user's path terminated in strings
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the values for the given keys in the given item and returns an array of those values
|
||||
* @param item - the item from which the values will be retrieved
|
||||
* @param keys - the keys to use to retrieve the values
|
||||
* @return objects with {itemValue, attributes}
|
||||
*/
|
||||
function getAllValuesToRank(item, keys) {
|
||||
const allValues = [];
|
||||
for (let j = 0, J = keys.length; j < J; j++) {
|
||||
const key = keys[j];
|
||||
const attributes = getKeyAttributes(key);
|
||||
const itemValues = getItemValues(item, key);
|
||||
for (let i = 0, I = itemValues.length; i < I; i++) {
|
||||
allValues.push({
|
||||
itemValue: itemValues[i],
|
||||
attributes
|
||||
});
|
||||
}
|
||||
}
|
||||
return allValues;
|
||||
}
|
||||
const defaultKeyAttributes = {
|
||||
maxRanking: Infinity,
|
||||
minRanking: -Infinity
|
||||
};
|
||||
/**
|
||||
* Gets all the attributes for the given key
|
||||
* @param key - the key from which the attributes will be retrieved
|
||||
* @return object containing the key's attributes
|
||||
*/
|
||||
function getKeyAttributes(key) {
|
||||
if (typeof key === 'string') {
|
||||
return defaultKeyAttributes;
|
||||
}
|
||||
return {
|
||||
...defaultKeyAttributes,
|
||||
...key
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
eslint
|
||||
no-continue: "off",
|
||||
*/
|
||||
|
||||
export { defaultBaseSortFn, matchSorter, rankings };
|
||||
+908
@@ -0,0 +1,908 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.matchSorter = {}));
|
||||
})(this, (function (exports) { 'use strict';
|
||||
|
||||
var removeAccents$1 = {exports: {}};
|
||||
|
||||
var characterMap = {
|
||||
"À": "A",
|
||||
"Á": "A",
|
||||
"Â": "A",
|
||||
"Ã": "A",
|
||||
"Ä": "A",
|
||||
"Å": "A",
|
||||
"Ấ": "A",
|
||||
"Ắ": "A",
|
||||
"Ẳ": "A",
|
||||
"Ẵ": "A",
|
||||
"Ặ": "A",
|
||||
"Æ": "AE",
|
||||
"Ầ": "A",
|
||||
"Ằ": "A",
|
||||
"Ȃ": "A",
|
||||
"Ả": "A",
|
||||
"Ạ": "A",
|
||||
"Ẩ": "A",
|
||||
"Ẫ": "A",
|
||||
"Ậ": "A",
|
||||
"Ç": "C",
|
||||
"Ḉ": "C",
|
||||
"È": "E",
|
||||
"É": "E",
|
||||
"Ê": "E",
|
||||
"Ë": "E",
|
||||
"Ế": "E",
|
||||
"Ḗ": "E",
|
||||
"Ề": "E",
|
||||
"Ḕ": "E",
|
||||
"Ḝ": "E",
|
||||
"Ȇ": "E",
|
||||
"Ẻ": "E",
|
||||
"Ẽ": "E",
|
||||
"Ẹ": "E",
|
||||
"Ể": "E",
|
||||
"Ễ": "E",
|
||||
"Ệ": "E",
|
||||
"Ì": "I",
|
||||
"Í": "I",
|
||||
"Î": "I",
|
||||
"Ï": "I",
|
||||
"Ḯ": "I",
|
||||
"Ȋ": "I",
|
||||
"Ỉ": "I",
|
||||
"Ị": "I",
|
||||
"Ð": "D",
|
||||
"Ñ": "N",
|
||||
"Ò": "O",
|
||||
"Ó": "O",
|
||||
"Ô": "O",
|
||||
"Õ": "O",
|
||||
"Ö": "O",
|
||||
"Ø": "O",
|
||||
"Ố": "O",
|
||||
"Ṍ": "O",
|
||||
"Ṓ": "O",
|
||||
"Ȏ": "O",
|
||||
"Ỏ": "O",
|
||||
"Ọ": "O",
|
||||
"Ổ": "O",
|
||||
"Ỗ": "O",
|
||||
"Ộ": "O",
|
||||
"Ờ": "O",
|
||||
"Ở": "O",
|
||||
"Ỡ": "O",
|
||||
"Ớ": "O",
|
||||
"Ợ": "O",
|
||||
"Ù": "U",
|
||||
"Ú": "U",
|
||||
"Û": "U",
|
||||
"Ü": "U",
|
||||
"Ủ": "U",
|
||||
"Ụ": "U",
|
||||
"Ử": "U",
|
||||
"Ữ": "U",
|
||||
"Ự": "U",
|
||||
"Ý": "Y",
|
||||
"à": "a",
|
||||
"á": "a",
|
||||
"â": "a",
|
||||
"ã": "a",
|
||||
"ä": "a",
|
||||
"å": "a",
|
||||
"ấ": "a",
|
||||
"ắ": "a",
|
||||
"ẳ": "a",
|
||||
"ẵ": "a",
|
||||
"ặ": "a",
|
||||
"æ": "ae",
|
||||
"ầ": "a",
|
||||
"ằ": "a",
|
||||
"ȃ": "a",
|
||||
"ả": "a",
|
||||
"ạ": "a",
|
||||
"ẩ": "a",
|
||||
"ẫ": "a",
|
||||
"ậ": "a",
|
||||
"ç": "c",
|
||||
"ḉ": "c",
|
||||
"è": "e",
|
||||
"é": "e",
|
||||
"ê": "e",
|
||||
"ë": "e",
|
||||
"ế": "e",
|
||||
"ḗ": "e",
|
||||
"ề": "e",
|
||||
"ḕ": "e",
|
||||
"ḝ": "e",
|
||||
"ȇ": "e",
|
||||
"ẻ": "e",
|
||||
"ẽ": "e",
|
||||
"ẹ": "e",
|
||||
"ể": "e",
|
||||
"ễ": "e",
|
||||
"ệ": "e",
|
||||
"ì": "i",
|
||||
"í": "i",
|
||||
"î": "i",
|
||||
"ï": "i",
|
||||
"ḯ": "i",
|
||||
"ȋ": "i",
|
||||
"ỉ": "i",
|
||||
"ị": "i",
|
||||
"ð": "d",
|
||||
"ñ": "n",
|
||||
"ò": "o",
|
||||
"ó": "o",
|
||||
"ô": "o",
|
||||
"õ": "o",
|
||||
"ö": "o",
|
||||
"ø": "o",
|
||||
"ố": "o",
|
||||
"ṍ": "o",
|
||||
"ṓ": "o",
|
||||
"ȏ": "o",
|
||||
"ỏ": "o",
|
||||
"ọ": "o",
|
||||
"ổ": "o",
|
||||
"ỗ": "o",
|
||||
"ộ": "o",
|
||||
"ờ": "o",
|
||||
"ở": "o",
|
||||
"ỡ": "o",
|
||||
"ớ": "o",
|
||||
"ợ": "o",
|
||||
"ù": "u",
|
||||
"ú": "u",
|
||||
"û": "u",
|
||||
"ü": "u",
|
||||
"ủ": "u",
|
||||
"ụ": "u",
|
||||
"ử": "u",
|
||||
"ữ": "u",
|
||||
"ự": "u",
|
||||
"ý": "y",
|
||||
"ÿ": "y",
|
||||
"Ā": "A",
|
||||
"ā": "a",
|
||||
"Ă": "A",
|
||||
"ă": "a",
|
||||
"Ą": "A",
|
||||
"ą": "a",
|
||||
"Ć": "C",
|
||||
"ć": "c",
|
||||
"Ĉ": "C",
|
||||
"ĉ": "c",
|
||||
"Ċ": "C",
|
||||
"ċ": "c",
|
||||
"Č": "C",
|
||||
"č": "c",
|
||||
"C̆": "C",
|
||||
"c̆": "c",
|
||||
"Ď": "D",
|
||||
"ď": "d",
|
||||
"Đ": "D",
|
||||
"đ": "d",
|
||||
"Ē": "E",
|
||||
"ē": "e",
|
||||
"Ĕ": "E",
|
||||
"ĕ": "e",
|
||||
"Ė": "E",
|
||||
"ė": "e",
|
||||
"Ę": "E",
|
||||
"ę": "e",
|
||||
"Ě": "E",
|
||||
"ě": "e",
|
||||
"Ĝ": "G",
|
||||
"Ǵ": "G",
|
||||
"ĝ": "g",
|
||||
"ǵ": "g",
|
||||
"Ğ": "G",
|
||||
"ğ": "g",
|
||||
"Ġ": "G",
|
||||
"ġ": "g",
|
||||
"Ģ": "G",
|
||||
"ģ": "g",
|
||||
"Ĥ": "H",
|
||||
"ĥ": "h",
|
||||
"Ħ": "H",
|
||||
"ħ": "h",
|
||||
"Ḫ": "H",
|
||||
"ḫ": "h",
|
||||
"Ĩ": "I",
|
||||
"ĩ": "i",
|
||||
"Ī": "I",
|
||||
"ī": "i",
|
||||
"Ĭ": "I",
|
||||
"ĭ": "i",
|
||||
"Į": "I",
|
||||
"į": "i",
|
||||
"İ": "I",
|
||||
"ı": "i",
|
||||
"IJ": "IJ",
|
||||
"ij": "ij",
|
||||
"Ĵ": "J",
|
||||
"ĵ": "j",
|
||||
"Ķ": "K",
|
||||
"ķ": "k",
|
||||
"Ḱ": "K",
|
||||
"ḱ": "k",
|
||||
"K̆": "K",
|
||||
"k̆": "k",
|
||||
"Ĺ": "L",
|
||||
"ĺ": "l",
|
||||
"Ļ": "L",
|
||||
"ļ": "l",
|
||||
"Ľ": "L",
|
||||
"ľ": "l",
|
||||
"Ŀ": "L",
|
||||
"ŀ": "l",
|
||||
"Ł": "l",
|
||||
"ł": "l",
|
||||
"Ḿ": "M",
|
||||
"ḿ": "m",
|
||||
"M̆": "M",
|
||||
"m̆": "m",
|
||||
"Ń": "N",
|
||||
"ń": "n",
|
||||
"Ņ": "N",
|
||||
"ņ": "n",
|
||||
"Ň": "N",
|
||||
"ň": "n",
|
||||
"ʼn": "n",
|
||||
"N̆": "N",
|
||||
"n̆": "n",
|
||||
"Ō": "O",
|
||||
"ō": "o",
|
||||
"Ŏ": "O",
|
||||
"ŏ": "o",
|
||||
"Ő": "O",
|
||||
"ő": "o",
|
||||
"Œ": "OE",
|
||||
"œ": "oe",
|
||||
"P̆": "P",
|
||||
"p̆": "p",
|
||||
"Ŕ": "R",
|
||||
"ŕ": "r",
|
||||
"Ŗ": "R",
|
||||
"ŗ": "r",
|
||||
"Ř": "R",
|
||||
"ř": "r",
|
||||
"R̆": "R",
|
||||
"r̆": "r",
|
||||
"Ȓ": "R",
|
||||
"ȓ": "r",
|
||||
"Ś": "S",
|
||||
"ś": "s",
|
||||
"Ŝ": "S",
|
||||
"ŝ": "s",
|
||||
"Ş": "S",
|
||||
"Ș": "S",
|
||||
"ș": "s",
|
||||
"ş": "s",
|
||||
"Š": "S",
|
||||
"š": "s",
|
||||
"Ţ": "T",
|
||||
"ţ": "t",
|
||||
"ț": "t",
|
||||
"Ț": "T",
|
||||
"Ť": "T",
|
||||
"ť": "t",
|
||||
"Ŧ": "T",
|
||||
"ŧ": "t",
|
||||
"T̆": "T",
|
||||
"t̆": "t",
|
||||
"Ũ": "U",
|
||||
"ũ": "u",
|
||||
"Ū": "U",
|
||||
"ū": "u",
|
||||
"Ŭ": "U",
|
||||
"ŭ": "u",
|
||||
"Ů": "U",
|
||||
"ů": "u",
|
||||
"Ű": "U",
|
||||
"ű": "u",
|
||||
"Ų": "U",
|
||||
"ų": "u",
|
||||
"Ȗ": "U",
|
||||
"ȗ": "u",
|
||||
"V̆": "V",
|
||||
"v̆": "v",
|
||||
"Ŵ": "W",
|
||||
"ŵ": "w",
|
||||
"Ẃ": "W",
|
||||
"ẃ": "w",
|
||||
"X̆": "X",
|
||||
"x̆": "x",
|
||||
"Ŷ": "Y",
|
||||
"ŷ": "y",
|
||||
"Ÿ": "Y",
|
||||
"Y̆": "Y",
|
||||
"y̆": "y",
|
||||
"Ź": "Z",
|
||||
"ź": "z",
|
||||
"Ż": "Z",
|
||||
"ż": "z",
|
||||
"Ž": "Z",
|
||||
"ž": "z",
|
||||
"ſ": "s",
|
||||
"ƒ": "f",
|
||||
"Ơ": "O",
|
||||
"ơ": "o",
|
||||
"Ư": "U",
|
||||
"ư": "u",
|
||||
"Ǎ": "A",
|
||||
"ǎ": "a",
|
||||
"Ǐ": "I",
|
||||
"ǐ": "i",
|
||||
"Ǒ": "O",
|
||||
"ǒ": "o",
|
||||
"Ǔ": "U",
|
||||
"ǔ": "u",
|
||||
"Ǖ": "U",
|
||||
"ǖ": "u",
|
||||
"Ǘ": "U",
|
||||
"ǘ": "u",
|
||||
"Ǚ": "U",
|
||||
"ǚ": "u",
|
||||
"Ǜ": "U",
|
||||
"ǜ": "u",
|
||||
"Ứ": "U",
|
||||
"ứ": "u",
|
||||
"Ṹ": "U",
|
||||
"ṹ": "u",
|
||||
"Ǻ": "A",
|
||||
"ǻ": "a",
|
||||
"Ǽ": "AE",
|
||||
"ǽ": "ae",
|
||||
"Ǿ": "O",
|
||||
"ǿ": "o",
|
||||
"Þ": "TH",
|
||||
"þ": "th",
|
||||
"Ṕ": "P",
|
||||
"ṕ": "p",
|
||||
"Ṥ": "S",
|
||||
"ṥ": "s",
|
||||
"X́": "X",
|
||||
"x́": "x",
|
||||
"Ѓ": "Г",
|
||||
"ѓ": "г",
|
||||
"Ќ": "К",
|
||||
"ќ": "к",
|
||||
"A̋": "A",
|
||||
"a̋": "a",
|
||||
"E̋": "E",
|
||||
"e̋": "e",
|
||||
"I̋": "I",
|
||||
"i̋": "i",
|
||||
"Ǹ": "N",
|
||||
"ǹ": "n",
|
||||
"Ồ": "O",
|
||||
"ồ": "o",
|
||||
"Ṑ": "O",
|
||||
"ṑ": "o",
|
||||
"Ừ": "U",
|
||||
"ừ": "u",
|
||||
"Ẁ": "W",
|
||||
"ẁ": "w",
|
||||
"Ỳ": "Y",
|
||||
"ỳ": "y",
|
||||
"Ȁ": "A",
|
||||
"ȁ": "a",
|
||||
"Ȅ": "E",
|
||||
"ȅ": "e",
|
||||
"Ȉ": "I",
|
||||
"ȉ": "i",
|
||||
"Ȍ": "O",
|
||||
"ȍ": "o",
|
||||
"Ȑ": "R",
|
||||
"ȑ": "r",
|
||||
"Ȕ": "U",
|
||||
"ȕ": "u",
|
||||
"B̌": "B",
|
||||
"b̌": "b",
|
||||
"Č̣": "C",
|
||||
"č̣": "c",
|
||||
"Ê̌": "E",
|
||||
"ê̌": "e",
|
||||
"F̌": "F",
|
||||
"f̌": "f",
|
||||
"Ǧ": "G",
|
||||
"ǧ": "g",
|
||||
"Ȟ": "H",
|
||||
"ȟ": "h",
|
||||
"J̌": "J",
|
||||
"ǰ": "j",
|
||||
"Ǩ": "K",
|
||||
"ǩ": "k",
|
||||
"M̌": "M",
|
||||
"m̌": "m",
|
||||
"P̌": "P",
|
||||
"p̌": "p",
|
||||
"Q̌": "Q",
|
||||
"q̌": "q",
|
||||
"Ř̩": "R",
|
||||
"ř̩": "r",
|
||||
"Ṧ": "S",
|
||||
"ṧ": "s",
|
||||
"V̌": "V",
|
||||
"v̌": "v",
|
||||
"W̌": "W",
|
||||
"w̌": "w",
|
||||
"X̌": "X",
|
||||
"x̌": "x",
|
||||
"Y̌": "Y",
|
||||
"y̌": "y",
|
||||
"A̧": "A",
|
||||
"a̧": "a",
|
||||
"B̧": "B",
|
||||
"b̧": "b",
|
||||
"Ḑ": "D",
|
||||
"ḑ": "d",
|
||||
"Ȩ": "E",
|
||||
"ȩ": "e",
|
||||
"Ɛ̧": "E",
|
||||
"ɛ̧": "e",
|
||||
"Ḩ": "H",
|
||||
"ḩ": "h",
|
||||
"I̧": "I",
|
||||
"i̧": "i",
|
||||
"Ɨ̧": "I",
|
||||
"ɨ̧": "i",
|
||||
"M̧": "M",
|
||||
"m̧": "m",
|
||||
"O̧": "O",
|
||||
"o̧": "o",
|
||||
"Q̧": "Q",
|
||||
"q̧": "q",
|
||||
"U̧": "U",
|
||||
"u̧": "u",
|
||||
"X̧": "X",
|
||||
"x̧": "x",
|
||||
"Z̧": "Z",
|
||||
"z̧": "z",
|
||||
"й": "и",
|
||||
"Й": "И",
|
||||
"ё": "е",
|
||||
"Ё": "Е"
|
||||
};
|
||||
var chars = Object.keys(characterMap).join('|');
|
||||
var allAccents = new RegExp(chars, 'g');
|
||||
var firstAccent = new RegExp(chars, '');
|
||||
function matcher(match) {
|
||||
return characterMap[match];
|
||||
}
|
||||
var removeAccents = function (string) {
|
||||
return string.replace(allAccents, matcher);
|
||||
};
|
||||
var hasAccents = function (string) {
|
||||
return !!string.match(firstAccent);
|
||||
};
|
||||
removeAccents$1.exports = removeAccents;
|
||||
removeAccents$1.exports.has = hasAccents;
|
||||
removeAccents$1.exports.remove = removeAccents;
|
||||
|
||||
/**
|
||||
* @name match-sorter
|
||||
* @license MIT license.
|
||||
* @copyright (c) 2020 Kent C. Dodds
|
||||
* @author Kent C. Dodds <me@kentcdodds.com> (https://kentcdodds.com)
|
||||
*/
|
||||
const rankings = {
|
||||
CASE_SENSITIVE_EQUAL: 7,
|
||||
EQUAL: 6,
|
||||
STARTS_WITH: 5,
|
||||
WORD_STARTS_WITH: 4,
|
||||
CONTAINS: 3,
|
||||
ACRONYM: 2,
|
||||
MATCHES: 1,
|
||||
NO_MATCH: 0
|
||||
};
|
||||
const defaultBaseSortFn = (a, b) => String(a.rankedValue).localeCompare(String(b.rankedValue));
|
||||
|
||||
/**
|
||||
* Takes an array of items and a value and returns a new array with the items that match the given value
|
||||
* @param {Array} items - the items to sort
|
||||
* @param {String} value - the value to use for ranking
|
||||
* @param {Object} options - Some options to configure the sorter
|
||||
* @return {Array} - the new sorted array
|
||||
*/
|
||||
function matchSorter(items, value, options) {
|
||||
if (options === void 0) {
|
||||
options = {};
|
||||
}
|
||||
const {
|
||||
keys,
|
||||
threshold = rankings.MATCHES,
|
||||
baseSort = defaultBaseSortFn,
|
||||
sorter = matchedItems => matchedItems.sort((a, b) => sortRankedValues(a, b, baseSort))
|
||||
} = options;
|
||||
const matchedItems = items.reduce(reduceItemsToRanked, []);
|
||||
return sorter(matchedItems).map(_ref => {
|
||||
let {
|
||||
item
|
||||
} = _ref;
|
||||
return item;
|
||||
});
|
||||
function reduceItemsToRanked(matches, item, index) {
|
||||
const rankingInfo = getHighestRanking(item, keys, value, options);
|
||||
const {
|
||||
rank,
|
||||
keyThreshold = threshold
|
||||
} = rankingInfo;
|
||||
if (rank >= keyThreshold) {
|
||||
matches.push({
|
||||
...rankingInfo,
|
||||
item,
|
||||
index
|
||||
});
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
matchSorter.rankings = rankings;
|
||||
|
||||
/**
|
||||
* 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 {{rank: Number, keyIndex: Number, keyThreshold: Number}} - the highest ranking
|
||||
*/
|
||||
function getHighestRanking(item, keys, value, options) {
|
||||
if (!keys) {
|
||||
// if keys is not specified, then we assume the item given is ready to be matched
|
||||
const stringItem = item;
|
||||
return {
|
||||
// ends up being duplicate of 'item' in matches but consistent
|
||||
rankedValue: stringItem,
|
||||
rank: getMatchRanking(stringItem, value, options),
|
||||
keyIndex: -1,
|
||||
keyThreshold: options.threshold
|
||||
};
|
||||
}
|
||||
const valuesToRank = getAllValuesToRank(item, keys);
|
||||
return valuesToRank.reduce((_ref2, _ref3, i) => {
|
||||
let {
|
||||
rank,
|
||||
rankedValue,
|
||||
keyIndex,
|
||||
keyThreshold
|
||||
} = _ref2;
|
||||
let {
|
||||
itemValue,
|
||||
attributes
|
||||
} = _ref3;
|
||||
let newRank = getMatchRanking(itemValue, value, options);
|
||||
let newRankedValue = rankedValue;
|
||||
const {
|
||||
minRanking,
|
||||
maxRanking,
|
||||
threshold
|
||||
} = attributes;
|
||||
if (newRank < minRanking && newRank >= rankings.MATCHES) {
|
||||
newRank = minRanking;
|
||||
} else if (newRank > maxRanking) {
|
||||
newRank = maxRanking;
|
||||
}
|
||||
if (newRank > rank) {
|
||||
rank = newRank;
|
||||
keyIndex = i;
|
||||
keyThreshold = threshold;
|
||||
newRankedValue = itemValue;
|
||||
}
|
||||
return {
|
||||
rankedValue: newRankedValue,
|
||||
rank,
|
||||
keyIndex,
|
||||
keyThreshold
|
||||
};
|
||||
}, {
|
||||
rankedValue: item,
|
||||
rank: rankings.NO_MATCH,
|
||||
keyIndex: -1,
|
||||
keyThreshold: options.threshold
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, options) {
|
||||
testString = prepareValueForComparison(testString, options);
|
||||
stringToRank = prepareValueForComparison(stringToRank, options);
|
||||
|
||||
// too long
|
||||
if (stringToRank.length > testString.length) {
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
|
||||
// case sensitive equals
|
||||
if (testString === stringToRank) {
|
||||
return rankings.CASE_SENSITIVE_EQUAL;
|
||||
}
|
||||
|
||||
// Lower casing before further comparison
|
||||
testString = testString.toLowerCase();
|
||||
stringToRank = stringToRank.toLowerCase();
|
||||
|
||||
// case insensitive equals
|
||||
if (testString === stringToRank) {
|
||||
return rankings.EQUAL;
|
||||
}
|
||||
|
||||
// starts with
|
||||
if (testString.startsWith(stringToRank)) {
|
||||
return rankings.STARTS_WITH;
|
||||
}
|
||||
|
||||
// word starts with
|
||||
if (testString.includes(` ${stringToRank}`)) {
|
||||
return rankings.WORD_STARTS_WITH;
|
||||
}
|
||||
|
||||
// contains
|
||||
if (testString.includes(stringToRank)) {
|
||||
return rankings.CONTAINS;
|
||||
} else if (stringToRank.length === 1) {
|
||||
// If the only character in the given stringToRank
|
||||
// isn't even contained in the testString, then
|
||||
// it's definitely not a match.
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
|
||||
// acronym
|
||||
if (getAcronym(testString).includes(stringToRank)) {
|
||||
return rankings.ACRONYM;
|
||||
}
|
||||
|
||||
// will return a number between rankings.MATCHES and
|
||||
// rankings.MATCHES + 1 depending on how close of a match it is.
|
||||
return getClosenessRanking(testString, stringToRank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an acronym for a string.
|
||||
*
|
||||
* @param {String} string the string for which to produce the acronym
|
||||
* @returns {String} the acronym
|
||||
*/
|
||||
function getAcronym(string) {
|
||||
let acronym = '';
|
||||
const wordsInString = string.split(' ');
|
||||
wordsInString.forEach(wordInString => {
|
||||
const splitByHyphenWords = wordInString.split('-');
|
||||
splitByHyphenWords.forEach(splitByHyphenWord => {
|
||||
acronym += splitByHyphenWord.substr(0, 1);
|
||||
});
|
||||
});
|
||||
return acronym;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a score based on how spread apart the
|
||||
* characters from the stringToRank are within the testString.
|
||||
* A number close to rankings.MATCHES represents a loose match. A number close
|
||||
* to rankings.MATCHES + 1 represents a tighter match.
|
||||
* @param {String} testString - the string to test against
|
||||
* @param {String} stringToRank - the string to rank
|
||||
* @returns {Number} the number between rankings.MATCHES and
|
||||
* rankings.MATCHES + 1 for how well stringToRank matches testString
|
||||
*/
|
||||
function getClosenessRanking(testString, stringToRank) {
|
||||
let matchingInOrderCharCount = 0;
|
||||
let charNumber = 0;
|
||||
function findMatchingCharacter(matchChar, string, index) {
|
||||
for (let j = index, J = string.length; j < J; j++) {
|
||||
const stringChar = string[j];
|
||||
if (stringChar === matchChar) {
|
||||
matchingInOrderCharCount += 1;
|
||||
return j + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
function getRanking(spread) {
|
||||
const spreadPercentage = 1 / spread;
|
||||
const inOrderPercentage = matchingInOrderCharCount / stringToRank.length;
|
||||
const ranking = rankings.MATCHES + inOrderPercentage * spreadPercentage;
|
||||
return ranking;
|
||||
}
|
||||
const firstIndex = findMatchingCharacter(stringToRank[0], testString, 0);
|
||||
if (firstIndex < 0) {
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
charNumber = firstIndex;
|
||||
for (let i = 1, I = stringToRank.length; i < I; i++) {
|
||||
const matchChar = stringToRank[i];
|
||||
charNumber = findMatchingCharacter(matchChar, testString, charNumber);
|
||||
const found = charNumber > -1;
|
||||
if (!found) {
|
||||
return rankings.NO_MATCH;
|
||||
}
|
||||
}
|
||||
const spread = charNumber - firstIndex;
|
||||
return getRanking(spread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts items that have a rank, index, and keyIndex
|
||||
* @param {Object} a - the first item to sort
|
||||
* @param {Object} b - the second item to sort
|
||||
* @return {Number} -1 if a should come first, 1 if b should come first, 0 if equal
|
||||
*/
|
||||
function sortRankedValues(a, b, baseSort) {
|
||||
const aFirst = -1;
|
||||
const bFirst = 1;
|
||||
const {
|
||||
rank: aRank,
|
||||
keyIndex: aKeyIndex
|
||||
} = a;
|
||||
const {
|
||||
rank: bRank,
|
||||
keyIndex: bKeyIndex
|
||||
} = b;
|
||||
const same = aRank === bRank;
|
||||
if (same) {
|
||||
if (aKeyIndex === bKeyIndex) {
|
||||
// use the base sort function as a tie-breaker
|
||||
return baseSort(a, b);
|
||||
} else {
|
||||
return aKeyIndex < bKeyIndex ? aFirst : bFirst;
|
||||
}
|
||||
} else {
|
||||
return aRank > bRank ? aFirst : bFirst;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares value for comparison by stringifying it, removing diacritics (if specified)
|
||||
* @param {String} value - the value to clean
|
||||
* @param {Object} options - {keepDiacritics: whether to remove diacritics}
|
||||
* @return {String} the prepared value
|
||||
*/
|
||||
function prepareValueForComparison(value, _ref4) {
|
||||
let {
|
||||
keepDiacritics
|
||||
} = _ref4;
|
||||
// value might not actually be a string at this point (we don't get to choose)
|
||||
// so part of preparing the value for comparison is ensure that it is a string
|
||||
value = `${value}`; // toString
|
||||
if (!keepDiacritics) {
|
||||
value = removeAccents$1.exports(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets value for key in item at arbitrarily nested keypath
|
||||
* @param {Object} item - the item
|
||||
* @param {Object|Function} key - the potentially nested keypath or property callback
|
||||
* @return {Array} - an array containing the value(s) at the nested keypath
|
||||
*/
|
||||
function getItemValues(item, key) {
|
||||
if (typeof key === 'object') {
|
||||
key = key.key;
|
||||
}
|
||||
let value;
|
||||
if (typeof key === 'function') {
|
||||
value = key(item);
|
||||
} else if (item == null) {
|
||||
value = null;
|
||||
} else if (Object.hasOwnProperty.call(item, key)) {
|
||||
value = item[key];
|
||||
} else if (key.includes('.')) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
return getNestedValues(key, item);
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
|
||||
// because `value` can also be undefined
|
||||
if (value == null) {
|
||||
return [];
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
return [String(value)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Given path: "foo.bar.baz"
|
||||
* And item: {foo: {bar: {baz: 'buzz'}}}
|
||||
* -> 'buzz'
|
||||
* @param path a dot-separated set of keys
|
||||
* @param item the item to get the value from
|
||||
*/
|
||||
function getNestedValues(path, item) {
|
||||
const keys = path.split('.');
|
||||
let values = [item];
|
||||
for (let i = 0, I = keys.length; i < I; i++) {
|
||||
const nestedKey = keys[i];
|
||||
let nestedValues = [];
|
||||
for (let j = 0, J = values.length; j < J; j++) {
|
||||
const nestedItem = values[j];
|
||||
if (nestedItem == null) continue;
|
||||
if (Object.hasOwnProperty.call(nestedItem, nestedKey)) {
|
||||
const nestedValue = nestedItem[nestedKey];
|
||||
if (nestedValue != null) {
|
||||
nestedValues.push(nestedValue);
|
||||
}
|
||||
} else if (nestedKey === '*') {
|
||||
// ensure that values is an array
|
||||
nestedValues = nestedValues.concat(nestedItem);
|
||||
}
|
||||
}
|
||||
values = nestedValues;
|
||||
}
|
||||
if (Array.isArray(values[0])) {
|
||||
// keep allowing the implicit wildcard for an array of strings at the end of
|
||||
// the path; don't use `.flat()` because that's not available in node.js v10
|
||||
const result = [];
|
||||
return result.concat(...values);
|
||||
}
|
||||
// Based on our logic it should be an array of strings by now...
|
||||
// assuming the user's path terminated in strings
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the values for the given keys in the given item and returns an array of those values
|
||||
* @param item - the item from which the values will be retrieved
|
||||
* @param keys - the keys to use to retrieve the values
|
||||
* @return objects with {itemValue, attributes}
|
||||
*/
|
||||
function getAllValuesToRank(item, keys) {
|
||||
const allValues = [];
|
||||
for (let j = 0, J = keys.length; j < J; j++) {
|
||||
const key = keys[j];
|
||||
const attributes = getKeyAttributes(key);
|
||||
const itemValues = getItemValues(item, key);
|
||||
for (let i = 0, I = itemValues.length; i < I; i++) {
|
||||
allValues.push({
|
||||
itemValue: itemValues[i],
|
||||
attributes
|
||||
});
|
||||
}
|
||||
}
|
||||
return allValues;
|
||||
}
|
||||
const defaultKeyAttributes = {
|
||||
maxRanking: Infinity,
|
||||
minRanking: -Infinity
|
||||
};
|
||||
/**
|
||||
* Gets all the attributes for the given key
|
||||
* @param key - the key from which the attributes will be retrieved
|
||||
* @return object containing the key's attributes
|
||||
*/
|
||||
function getKeyAttributes(key) {
|
||||
if (typeof key === 'string') {
|
||||
return defaultKeyAttributes;
|
||||
}
|
||||
return {
|
||||
...defaultKeyAttributes,
|
||||
...key
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
eslint
|
||||
no-continue: "off",
|
||||
*/
|
||||
|
||||
exports.defaultBaseSortFn = defaultBaseSortFn;
|
||||
exports.matchSorter = matchSorter;
|
||||
exports.rankings = rankings;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=match-sorter.umd.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+9
@@ -0,0 +1,9 @@
|
||||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).matchSorter={})}(this,(function(e){"use strict";var n={exports:{}},t={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","Ấ":"A","Ắ":"A","Ẳ":"A","Ẵ":"A","Ặ":"A","Æ":"AE","Ầ":"A","Ằ":"A","Ȃ":"A","Ả":"A","Ạ":"A","Ẩ":"A","Ẫ":"A","Ậ":"A","Ç":"C","Ḉ":"C","È":"E","É":"E","Ê":"E","Ë":"E","Ế":"E","Ḗ":"E","Ề":"E","Ḕ":"E","Ḝ":"E","Ȇ":"E","Ẻ":"E","Ẽ":"E","Ẹ":"E","Ể":"E","Ễ":"E","Ệ":"E","Ì":"I","Í":"I","Î":"I","Ï":"I","Ḯ":"I","Ȋ":"I","Ỉ":"I","Ị":"I","Ð":"D","Ñ":"N","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","Ố":"O","Ṍ":"O","Ṓ":"O","Ȏ":"O","Ỏ":"O","Ọ":"O","Ổ":"O","Ỗ":"O","Ộ":"O","Ờ":"O","Ở":"O","Ỡ":"O","Ớ":"O","Ợ":"O","Ù":"U","Ú":"U","Û":"U","Ü":"U","Ủ":"U","Ụ":"U","Ử":"U","Ữ":"U","Ự":"U","Ý":"Y","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","ấ":"a","ắ":"a","ẳ":"a","ẵ":"a","ặ":"a","æ":"ae","ầ":"a","ằ":"a","ȃ":"a","ả":"a","ạ":"a","ẩ":"a","ẫ":"a","ậ":"a","ç":"c","ḉ":"c","è":"e","é":"e","ê":"e","ë":"e","ế":"e","ḗ":"e","ề":"e","ḕ":"e","ḝ":"e","ȇ":"e","ẻ":"e","ẽ":"e","ẹ":"e","ể":"e","ễ":"e","ệ":"e","ì":"i","í":"i","î":"i","ï":"i","ḯ":"i","ȋ":"i","ỉ":"i","ị":"i","ð":"d","ñ":"n","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","ố":"o","ṍ":"o","ṓ":"o","ȏ":"o","ỏ":"o","ọ":"o","ổ":"o","ỗ":"o","ộ":"o","ờ":"o","ở":"o","ỡ":"o","ớ":"o","ợ":"o","ù":"u","ú":"u","û":"u","ü":"u","ủ":"u","ụ":"u","ử":"u","ữ":"u","ự":"u","ý":"y","ÿ":"y","Ā":"A","ā":"a","Ă":"A","ă":"a","Ą":"A","ą":"a","Ć":"C","ć":"c","Ĉ":"C","ĉ":"c","Ċ":"C","ċ":"c","Č":"C","č":"c","C̆":"C","c̆":"c","Ď":"D","ď":"d","Đ":"D","đ":"d","Ē":"E","ē":"e","Ĕ":"E","ĕ":"e","Ė":"E","ė":"e","Ę":"E","ę":"e","Ě":"E","ě":"e","Ĝ":"G","Ǵ":"G","ĝ":"g","ǵ":"g","Ğ":"G","ğ":"g","Ġ":"G","ġ":"g","Ģ":"G","ģ":"g","Ĥ":"H","ĥ":"h","Ħ":"H","ħ":"h","Ḫ":"H","ḫ":"h","Ĩ":"I","ĩ":"i","Ī":"I","ī":"i","Ĭ":"I","ĭ":"i","Į":"I","į":"i","İ":"I","ı":"i","IJ":"IJ","ij":"ij","Ĵ":"J","ĵ":"j","Ķ":"K","ķ":"k","Ḱ":"K","ḱ":"k","K̆":"K","k̆":"k","Ĺ":"L","ĺ":"l","Ļ":"L","ļ":"l","Ľ":"L","ľ":"l","Ŀ":"L","ŀ":"l","Ł":"l","ł":"l","Ḿ":"M","ḿ":"m","M̆":"M","m̆":"m","Ń":"N","ń":"n","Ņ":"N","ņ":"n","Ň":"N","ň":"n","ʼn":"n","N̆":"N","n̆":"n","Ō":"O","ō":"o","Ŏ":"O","ŏ":"o","Ő":"O","ő":"o","Œ":"OE","œ":"oe","P̆":"P","p̆":"p","Ŕ":"R","ŕ":"r","Ŗ":"R","ŗ":"r","Ř":"R","ř":"r","R̆":"R","r̆":"r","Ȓ":"R","ȓ":"r","Ś":"S","ś":"s","Ŝ":"S","ŝ":"s","Ş":"S","Ș":"S","ș":"s","ş":"s","Š":"S","š":"s","Ţ":"T","ţ":"t","ț":"t","Ț":"T","Ť":"T","ť":"t","Ŧ":"T","ŧ":"t","T̆":"T","t̆":"t","Ũ":"U","ũ":"u","Ū":"U","ū":"u","Ŭ":"U","ŭ":"u","Ů":"U","ů":"u","Ű":"U","ű":"u","Ų":"U","ų":"u","Ȗ":"U","ȗ":"u","V̆":"V","v̆":"v","Ŵ":"W","ŵ":"w","Ẃ":"W","ẃ":"w","X̆":"X","x̆":"x","Ŷ":"Y","ŷ":"y","Ÿ":"Y","Y̆":"Y","y̆":"y","Ź":"Z","ź":"z","Ż":"Z","ż":"z","Ž":"Z","ž":"z","ſ":"s","ƒ":"f","Ơ":"O","ơ":"o","Ư":"U","ư":"u","Ǎ":"A","ǎ":"a","Ǐ":"I","ǐ":"i","Ǒ":"O","ǒ":"o","Ǔ":"U","ǔ":"u","Ǖ":"U","ǖ":"u","Ǘ":"U","ǘ":"u","Ǚ":"U","ǚ":"u","Ǜ":"U","ǜ":"u","Ứ":"U","ứ":"u","Ṹ":"U","ṹ":"u","Ǻ":"A","ǻ":"a","Ǽ":"AE","ǽ":"ae","Ǿ":"O","ǿ":"o","Þ":"TH","þ":"th","Ṕ":"P","ṕ":"p","Ṥ":"S","ṥ":"s","X́":"X","x́":"x","Ѓ":"Г","ѓ":"г","Ќ":"К","ќ":"к","A̋":"A","a̋":"a","E̋":"E","e̋":"e","I̋":"I","i̋":"i","Ǹ":"N","ǹ":"n","Ồ":"O","ồ":"o","Ṑ":"O","ṑ":"o","Ừ":"U","ừ":"u","Ẁ":"W","ẁ":"w","Ỳ":"Y","ỳ":"y","Ȁ":"A","ȁ":"a","Ȅ":"E","ȅ":"e","Ȉ":"I","ȉ":"i","Ȍ":"O","ȍ":"o","Ȑ":"R","ȑ":"r","Ȕ":"U","ȕ":"u","B̌":"B","b̌":"b","Č̣":"C","č̣":"c","Ê̌":"E","ê̌":"e","F̌":"F","f̌":"f","Ǧ":"G","ǧ":"g","Ȟ":"H","ȟ":"h","J̌":"J","ǰ":"j","Ǩ":"K","ǩ":"k","M̌":"M","m̌":"m","P̌":"P","p̌":"p","Q̌":"Q","q̌":"q","Ř̩":"R","ř̩":"r","Ṧ":"S","ṧ":"s","V̌":"V","v̌":"v","W̌":"W","w̌":"w","X̌":"X","x̌":"x","Y̌":"Y","y̌":"y","A̧":"A","a̧":"a","B̧":"B","b̧":"b","Ḑ":"D","ḑ":"d","Ȩ":"E","ȩ":"e","Ɛ̧":"E","ɛ̧":"e","Ḩ":"H","ḩ":"h","I̧":"I","i̧":"i","Ɨ̧":"I","ɨ̧":"i","M̧":"M","m̧":"m","O̧":"O","o̧":"o","Q̧":"Q","q̧":"q","U̧":"U","u̧":"u","X̧":"X","x̧":"x","Z̧":"Z","z̧":"z","й":"и","Й":"И","ё":"е","Ё":"Е"},r=Object.keys(t).join("|"),o=new RegExp(r,"g"),u=new RegExp(r,"");function i(e){return t[e]}var a=function(e){return e.replace(o,i)};n.exports=a,n.exports.has=function(e){return!!e.match(u)},n.exports.remove=a;
|
||||
/**
|
||||
* @name match-sorter
|
||||
* @license MIT license.
|
||||
* @copyright (c) 2020 Kent C. Dodds
|
||||
* @author Kent C. Dodds <me@kentcdodds.com> (https://kentcdodds.com)
|
||||
*/
|
||||
const l={CASE_SENSITIVE_EQUAL:7,EQUAL:6,STARTS_WITH:5,WORD_STARTS_WITH:4,CONTAINS:3,ACRONYM:2,MATCHES:1,NO_MATCH:0},s=(e,n)=>String(e.rankedValue).localeCompare(String(n.rankedValue));function c(e,n,t){void 0===t&&(t={});const{keys:r,threshold:o=l.MATCHES,baseSort:u=s,sorter:i=(e=>e.sort(((e,n)=>f(e,n,u))))}=t,a=e.reduce((function(e,u,i){const a=function(e,n,t,r){if(!n){return{rankedValue:e,rank:A(e,t,r),keyIndex:-1,keyThreshold:r.threshold}}const o=function(e,n){const t=[];for(let r=0,o=n.length;r<o;r++){const o=n[r],u=d(o),i=h(e,o);for(let e=0,n=i.length;e<n;e++)t.push({itemValue:i[e],attributes:u})}return t}(e,n);return o.reduce(((e,n,o)=>{let{rank:u,rankedValue:i,keyIndex:a,keyThreshold:s}=e,{itemValue:c,attributes:f}=n,O=A(c,t,r),h=i;const{minRanking:E,maxRanking:d,threshold:T}=f;return O<E&&O>=l.MATCHES?O=E:O>d&&(O=d),O>u&&(u=O,a=o,s=T,h=c),{rankedValue:h,rank:u,keyIndex:a,keyThreshold:s}}),{rankedValue:e,rank:l.NO_MATCH,keyIndex:-1,keyThreshold:r.threshold})}(u,r,n,t),{rank:s,keyThreshold:c=o}=a;return s>=c&&e.push({...a,item:u,index:i}),e}),[]);return i(a).map((e=>{let{item:n}=e;return n}))}function A(e,n,t){return e=O(e,t),(n=O(n,t)).length>e.length?l.NO_MATCH:e===n?l.CASE_SENSITIVE_EQUAL:(e=e.toLowerCase())===(n=n.toLowerCase())?l.EQUAL:e.startsWith(n)?l.STARTS_WITH:e.includes(` ${n}`)?l.WORD_STARTS_WITH:e.includes(n)?l.CONTAINS:1===n.length?l.NO_MATCH:function(e){let n="";return e.split(" ").forEach((e=>{e.split("-").forEach((e=>{n+=e.substr(0,1)}))})),n}(e).includes(n)?l.ACRONYM:function(e,n){let t=0,r=0;function o(e,n,r){for(let o=r,u=n.length;o<u;o++){if(n[o]===e)return t+=1,o+1}return-1}function u(e){const r=1/e,o=t/n.length;return l.MATCHES+o*r}const i=o(n[0],e,0);if(i<0)return l.NO_MATCH;r=i;for(let t=1,u=n.length;t<u;t++){r=o(n[t],e,r);if(!(r>-1))return l.NO_MATCH}return u(r-i)}(e,n)}function f(e,n,t){const{rank:r,keyIndex:o}=e,{rank:u,keyIndex:i}=n;return r===u?o===i?t(e,n):o<i?-1:1:r>u?-1:1}function O(e,t){let{keepDiacritics:r}=t;return e=`${e}`,r||(e=n.exports(e)),e}function h(e,n){let t;if("object"==typeof n&&(n=n.key),"function"==typeof n)t=n(e);else if(null==e)t=null;else if(Object.hasOwnProperty.call(e,n))t=e[n];else{if(n.includes("."))return function(e,n){const t=e.split(".");let r=[n];for(let e=0,n=t.length;e<n;e++){const n=t[e];let o=[];for(let e=0,t=r.length;e<t;e++){const t=r[e];if(null!=t)if(Object.hasOwnProperty.call(t,n)){const e=t[n];null!=e&&o.push(e)}else"*"===n&&(o=o.concat(t))}r=o}if(Array.isArray(r[0])){return[].concat(...r)}return r}(n,e);t=null}return null==t?[]:Array.isArray(t)?t:[String(t)]}c.rankings=l;const E={maxRanking:1/0,minRanking:-1/0};function d(e){return"string"==typeof e?E:{...E,...e}}e.defaultBaseSortFn=s,e.matchSorter=c,e.rankings=l,Object.defineProperty(e,"__esModule",{value:!0})}));
|
||||
//# sourceMappingURL=match-sorter.umd.min.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+59
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "match-sorter",
|
||||
"version": "6.3.4",
|
||||
"description": "Simple, expected, and deterministic best-match sorting of an array in JavaScript",
|
||||
"main": "dist/match-sorter.cjs.js",
|
||||
"module": "dist/match-sorter.esm.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "kcd-scripts build --bundle --environment BUILD_NAME:matchSorter",
|
||||
"lint": "kcd-scripts lint",
|
||||
"setup": "npm install && npm run validate -s",
|
||||
"test": "kcd-scripts test",
|
||||
"typecheck": "kcd-scripts typecheck",
|
||||
"test:update": "npm test -- --updateSnapshot --coverage",
|
||||
"validate": "kcd-scripts validate"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"keywords": [
|
||||
"autocomplete",
|
||||
"filter list",
|
||||
"sort",
|
||||
"advanced sort",
|
||||
"user intuitive sort"
|
||||
],
|
||||
"author": "Kent C. Dodds <me@kentcdodds.com> (https://kentcdodds.com)",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.8",
|
||||
"remove-accents": "0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.15",
|
||||
"kcd-scripts": "^15",
|
||||
"typescript": "^5.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"./node_modules/kcd-scripts/eslint.js"
|
||||
],
|
||||
"rules": {
|
||||
"prefer-object-has-own": "off"
|
||||
}
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"node_modules",
|
||||
"coverage",
|
||||
"dist"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kentcdodds/match-sorter"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/kentcdodds/match-sorter/issues"
|
||||
},
|
||||
"homepage": "https://github.com/kentcdodds/match-sorter#readme"
|
||||
}
|
||||
Reference in New Issue
Block a user