Вот навскидку накидал франкенштейна(больше под свои задачи):
function escapeRegExp(string) {
return String(string).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// для оптимизации неплохо бы завернуть в memoize-one
const toSpacedRegExp = (needle, ignoreCase = true) => new RegExp(
needle
.trim()
.split(/\s+/)
.map(chunk => '(' + escapeRegExp(chunk) + ')')
.join('(\\S*(?:\\s+\\S*)+?)'),
ignoreCase ? 'i' : ''
);
// для оптимизации неплохо бы завернуть в memoize-one
const toSomeRegExp = (needle, ignoreCase = true) => new RegExp(
needle
.trim()
.split(/\s+/)
.map(chunk => '(' + escapeRegExp(chunk) + ')')
.join('([\\S\\s]*)'),
ignoreCase ? 'i' : ''
);
// Возвращает либо false, либо массив разделённых совпадний
// smartFind('start found rest', 'found') // ['start ', 'found', ' rest']
// smartFind('start found rest', 'start') // ['', 'start', ' found rest']
// удобно для подсвечивания найденного тупо через i % 2
function smartFind(haystack, needle) {
if (!haystack) return false;
if (!needle) return ['', haystack, ''];
// ищем сначала разделённые пробелами: ва ся => ВАся СЯпкин
let result = haystack.split(toSpacedRegExp(needle));
if (result.length > 1) return result;
// ищем хоть какое-то совпадение: ва ся => ВАСЯ пупкин
result = haystack.split(toSomeRegExp(needle));
return result.length > 1 && result;
}
По ключевым словам "fuzzy search" можно найти куда оптимальнее и красивее вещи, но тут уж сам.:)