diff --git a/src/autocomplete/QueryMatcher.ts b/src/autocomplete/QueryMatcher.ts index 4b8c1141fd..a11928c1dd 100644 --- a/src/autocomplete/QueryMatcher.ts +++ b/src/autocomplete/QueryMatcher.ts @@ -17,7 +17,6 @@ limitations under the License. */ import _at from 'lodash/at'; -import _flatMap from 'lodash/flatMap'; import _uniq from 'lodash/uniq'; function stripDiacritics(str: string): string { @@ -49,7 +48,7 @@ export default class QueryMatcher { private _options: IOptions; private _keys: IOptions["keys"]; private _funcs: Required["funcs"]>; - private _items: Map<{value: string, weight: number}, T[]>; + private _items: Map; constructor(objects: T[], options: IOptions = { keys: [] }) { this._options = options; @@ -87,14 +86,14 @@ export default class QueryMatcher { for (const [index, keyValue] of Object.entries(keyValues)) { if (!keyValue) continue; // skip falsy keyValues - const key = { - value: stripDiacritics(keyValue).toLowerCase(), - weight: Number(index) - }; + const key = stripDiacritics(keyValue).toLowerCase(); if (!this._items.has(key)) { this._items.set(key, []); } - this._items.get(key).push(object); + this._items.get(key).push({ + keyWeight: Number(index), + object, + }); } } } @@ -107,35 +106,40 @@ export default class QueryMatcher { if (query.length === 0) { return []; } - const results = []; + const matches = []; // Iterate through the map & check each key. // ES6 Map iteration order is defined to be insertion order, so results // here will come out in the order they were put in. - for (const key of this._items.keys()) { - let {value: resultKey} = key; + for (const [key, candidates] of this._items.entries()) { + let resultKey = key; if (this._options.shouldMatchWordsOnly) { resultKey = resultKey.replace(/[^\w]/g, ''); } const index = resultKey.indexOf(query); if (index !== -1 && (!this._options.shouldMatchPrefix || index === 0)) { - results.push({key, index}); + matches.push( + ...candidates.map((candidate) => ({key, index, ...candidate})) + ); } } - // Sort them by where the query appeared in the search key, then by + // Sort matches by where the query appeared in the search key, then by // where the matched key appeared in the provided array of keys. - const sortedResults = results.slice().sort((a, b) => { + matches.sort((a, b) => { if (a.index < b.index) { return -1; - } else if (a.index === b.index && a.key.weight < b.key.weight) { - return -1; + } else if (a.index === b.index) { + if (a.keyWeight < b.keyWeight) { + return -1; + } else if (a.keyWeight === b.keyWeight) { + return 0; + } } + return 1; }); - // Now map the keys to the result objects. Each result object is a list, so - // flatMap will flatten those lists out into a single list. Also remove any - // duplicates. - return _uniq(_flatMap(sortedResults, (candidate) => this._items.get(candidate.key))); + // Now map the keys to the result objects. Also remove any duplicates. + return _uniq(matches.map((match) => match.object)); } }