keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
parent
09568e1b65
commit
5a89830556
@ -0,0 +1,69 @@ |
|||||||
|
import { matchSort } from '..' |
||||||
|
|
||||||
|
it('does not sort items when query is empty', () => { |
||||||
|
const items = [ |
||||||
|
{ name: 'app' }, |
||||||
|
{ name: 'apple' }, |
||||||
|
] |
||||||
|
const query = '' |
||||||
|
const result = matchSort( |
||||||
|
items, |
||||||
|
'name', |
||||||
|
query, |
||||||
|
) |
||||||
|
expect(result).toEqual(items) |
||||||
|
}) |
||||||
|
|
||||||
|
it('non matching words omitted', () => { |
||||||
|
const items = [ |
||||||
|
{ name: 'banana' }, |
||||||
|
{ name: 'app' }, |
||||||
|
{ name: 'apple' }, |
||||||
|
] |
||||||
|
const query = 'app' |
||||||
|
const result = matchSort( |
||||||
|
items, |
||||||
|
'name', |
||||||
|
query, |
||||||
|
) |
||||||
|
expect(result).toEqual([ |
||||||
|
items[1], |
||||||
|
items[2], |
||||||
|
]) |
||||||
|
}) |
||||||
|
|
||||||
|
it('matching words go first', () => { |
||||||
|
const items = [ |
||||||
|
{ name: 'apple' }, |
||||||
|
{ name: 'app' }, |
||||||
|
] |
||||||
|
const query = 'app' |
||||||
|
const result = matchSort( |
||||||
|
items, |
||||||
|
'name', |
||||||
|
query, |
||||||
|
) |
||||||
|
expect(result).toEqual([ |
||||||
|
items[1], |
||||||
|
items[0], |
||||||
|
]) |
||||||
|
}) |
||||||
|
|
||||||
|
it('words starting with query word go second', () => { |
||||||
|
const items = [ |
||||||
|
{ name: 'apple' }, |
||||||
|
{ name: 'app' }, |
||||||
|
{ name: 'buggy app' }, |
||||||
|
] |
||||||
|
const query = 'app' |
||||||
|
const result = matchSort( |
||||||
|
items, |
||||||
|
'name', |
||||||
|
query, |
||||||
|
) |
||||||
|
expect(result).toEqual([ |
||||||
|
items[1], |
||||||
|
items[0], |
||||||
|
items[2], |
||||||
|
]) |
||||||
|
}) |
||||||
@ -0,0 +1,77 @@ |
|||||||
|
import startsWith from 'lodash/startsWith' |
||||||
|
import orderBy from 'lodash/orderBy' |
||||||
|
import toLower from 'lodash/toLower' |
||||||
|
import filter from 'lodash/filter' |
||||||
|
import split from 'lodash/split' |
||||||
|
import size from 'lodash/size' |
||||||
|
import some from 'lodash/some' |
||||||
|
|
||||||
|
type Item = { [key: string]: any } |
||||||
|
|
||||||
|
type Func = <T extends Item, K extends keyof T>( |
||||||
|
items: Array<T>, |
||||||
|
key: K, |
||||||
|
query: string, |
||||||
|
) => Array<T> |
||||||
|
|
||||||
|
const startsWithIgnore = (word: string, target: string) => ( |
||||||
|
startsWith(toLower(word), toLower(target)) |
||||||
|
) |
||||||
|
|
||||||
|
const hasManyWords = (word: string) => size(split(word, ' ')) > 1 |
||||||
|
|
||||||
|
const atLeastOneWordStartsWith = (word: string, target: string) => { |
||||||
|
const words = split(word, ' ') |
||||||
|
return some(words, (w) => startsWithIgnore(w, target)) |
||||||
|
} |
||||||
|
|
||||||
|
const filterMatching: Func = ( |
||||||
|
items, |
||||||
|
key, |
||||||
|
query, |
||||||
|
) => ( |
||||||
|
filter(items, (item) => { |
||||||
|
const value = item[key] |
||||||
|
if (startsWithIgnore(value, query)) return true |
||||||
|
if (hasManyWords(value)) return atLeastOneWordStartsWith(value, query) |
||||||
|
return false |
||||||
|
}) |
||||||
|
) |
||||||
|
|
||||||
|
const sortMatching: Func = ( |
||||||
|
items, |
||||||
|
key, |
||||||
|
query, |
||||||
|
) => ( |
||||||
|
orderBy( |
||||||
|
items, |
||||||
|
(item) => { |
||||||
|
if (item[key] === query) return 10 |
||||||
|
if (startsWithIgnore(item[key], query)) { |
||||||
|
return 5 |
||||||
|
} |
||||||
|
return 0 |
||||||
|
}, |
||||||
|
'desc', |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
export const matchSort: Func = ( |
||||||
|
items, |
||||||
|
key, |
||||||
|
query, |
||||||
|
) => { |
||||||
|
if (!query) return items |
||||||
|
|
||||||
|
const filteredItems = filterMatching( |
||||||
|
items, |
||||||
|
key, |
||||||
|
query, |
||||||
|
) |
||||||
|
|
||||||
|
return sortMatching( |
||||||
|
filteredItems, |
||||||
|
key, |
||||||
|
query, |
||||||
|
) |
||||||
|
} |
||||||
Loading…
Reference in new issue