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