//@flow import _at from 'lodash/at'; import _flatMap from 'lodash/flatMap'; import _sortBy from 'lodash/sortBy'; import _sortedUniq from 'lodash/sortedUniq'; import _keys from 'lodash/keys'; class KeyMap { keys: Array; objectMap: {[String]: Array}; priorityMap = new Map(); } export default class QueryMatcher { /** * @param {object[]} objects the objects to perform a match on * @param {string[]} keys an array of keys within each object to match on * Keys can refer to object properties by name and as in JavaScript (for nested properties) * * To use, simply presort objects by required criteria, run through this function and create a QueryMatcher with the * resulting KeyMap. * * TODO: Handle arrays and objects (Fuse did this, RoomProvider uses it) * @return {KeyMap} */ static valuesToKeyMap(objects: Array, keys: Array): KeyMap { const keyMap = new KeyMap(); const map = {}; objects.forEach((object, i) => { const keyValues = _at(object, keys); for (const keyValue of keyValues) { if (!map.hasOwnProperty(keyValue)) { map[keyValue] = []; } map[keyValue].push(object); } keyMap.priorityMap.set(object, i); }); keyMap.objectMap = map; keyMap.keys = _keys(map); return keyMap; } constructor(objects: Array, options: {[Object]: Object} = {}) { this.options = options; this.keys = options.keys; this.setObjects(objects); } setObjects(objects: Array) { this.keyMap = QueryMatcher.valuesToKeyMap(objects, this.keys); } match(query: String): Array { query = query.toLowerCase().replace(/[^\w]/g, ''); const results = _sortedUniq(_sortBy(_flatMap(this.keyMap.keys, (key) => { return key.toLowerCase().replace(/[^\w]/g, '').indexOf(query) >= 0 ? this.keyMap.objectMap[key] : []; }), (candidate) => this.keyMap.priorityMap.get(candidate))); return results; } }