Let\'s assume I have a huge (1000+) list of objects like this:
[{name: \'john dow\', age: 38, gender:\'m\'}, {name: \'jane dow\', age: 18, gender:\'f\'}, ..]
From experience, the following algorithm works quite well:
When the user types the first letter, you perform a search using Array.filter()
perhaps and store that result under whatever the user types (e.g. "j");
When the user types another letter (e.g. "o"), you perform the search on whatever was typed before ("j"), reducing the number of items to go through
When the user deletes one or more characters you try to find the stored searches based on whatever is left in the search box; if all fails, you show an empty list and invalidate the previously stored searches.
From simple to more complex (no additional libs):
//clear previous timed filtering
if (typingTimeout) {
clearTimeout(typingTimeout);
typingTimeout = null;
}
//schedule new filtering
typingTimeout = setTimeout(() => {
// do some filtering
}, 2000); //acceptable 1-2s
{
A: ['Aron', 'Arnold', ...],
B: ['Baby'],
....
}
Then filter them using the prefixed lists with the first char entered. It is just an example, you should group your data as you see fit (maybe first 2 letters...).
Here is a function that I implemented to help prefix my array:
export function prefixStringsArray(arr, prefixLength) {
const prefixArr = arr.map((s) => s.substr(0, prefixLength));
const filter = (item, query) =>
item.toLowerCase().startsWith(query.toLowerCase());
const prefixObj = {};
for (const pre of prefixArr) {
prefixObj[pre] = arr.filter((item) => filter(item, pre));
}
return prefixObj;
}
You should know that using JS object is a very good bet because they are accessed in O(1) (it is sort of a hash map) and if you group your data optimally you'll get small arrays to filter.
Notes:
query.length < prefixLength
Although a substring index (such as a Suffix tree) would make this faster, the direct search would be:
function (s, l) {
return l.filter(function (v) {
return v.name.find(s) !== -1;
});
}
where s
is the query string and l
is the list of objects.
I wouldn't worry too much about performance in this case. A desktop computer should eat up 1000, or 10,000 evaluations without sweat. I would avoid any kind of complex optimisation because the risk of breaking functionality is probably higher than the benefit of slightly efficient processing.
Javascript (ECMAScript 5) does provide new methods for filtering arrays. As a native method it is supposed to be a little faster.
var regex = ...
results = json.filter(function(result) {
return regex.test(result.name)
}
Array.prototype.filter is supported in modern browsers, see http://kangax.github.com/es5-compat-table/. A patch for older browsers is can be added with this: https://github.com/kriskowal/es5-shim