Выделите совпадения поисковых слов в целевой строке слов

#javascript #html #regex #search #full-text-search

Вопрос:

Задача: Учитывая поисковый запрос, состоящий из одного или нескольких слов, выделите каждое из слов, найденных в целевой строке из одного или нескольких слов. Обратите внимание, что поисковые слова могут отображаться в любом порядке и должны совпадать, даже если не все слова найдены в целевой строке.

Причина: Чтобы помочь пользователю визуализировать, почему целевая строка была сопоставлена с поисковым запросом, выделив совпадения.

Требование: Я также хочу, чтобы это соответствовало регистру без учета регистра.

Он должен обрабатывать простые случаи

Иглы: Foo bar
Сено: I pity the fool
Желаемый результат: Мне жаль фу л

Он должен соответствовать нескольким поисковым словам, разделенным строкой, в любом порядке в целевой строке

Иглы: Foo bar
Сено: I pity the bar fool
Желаемый результат: Мне жаль бар фу л

Он должен обрабатывать случаи, когда условия поиска могут конфликтовать друг с другом

Иглы: Barbeque bar
Сено: Quality barbeque grill
Желаемый результат: Качественный гриль для барбекю

Ответ №1:

Мое решение здесь основано на:

  1. Разделение поискового запроса на поисковые слова.
  2. Один за другим просматривая целевую строку, чтобы увидеть, существует ли в ней поисковое слово. Это делается путем разделения по поисковому слову.
    • Те, у кого нечетный индекс, будут словом(ами), которые нужно выделить (если совпадения не найдено, результат будет иметь длину 1 , а у этого элемента будет индекс 0 ).
  3. Передача результатов на шаг 2, но только попытка выделить в целевой строке части, которые еще не выделены.

Лично я затем использую выходной массив непосредственно с Vue и позволяю ему выполнять задачи создания шаблонов, но включил здесь пример того, как он может выводить простой HTML для визуализации.

 <script lang="js">
  const splitHighlight = (arrayOfStrings, highlightWord) => {
    // use () for capture and keeping the matched string, and `i` for case
    // insensitive matching
    const regex = new RegExp(`(${highlightWord})`, "i");
    return arrayOfStrings
      .map((s, i) => {
        // if the index is odd then then this string is already highlighted,
        // so don't try to highlight within the highlight
        const isAlreadyHighlighted = i % 2 !== 0;
        return isAlreadyHighlighted ? [s] : s.split(regex);
      })
      .flat();
  };

  const splitNeedlesInHay = (needlesStr, hay) => {
    const needlesArray = needlesStr.trim().split(" ");
    let hayArray = [hay];
    needlesArray.forEach((n) => (hayArray = splitHighlight(hayArray, n)));
    return hayArray;
  };

  const highlightNeedlesInHay = (needlesStr, hay) => {
    const needlesInHay = splitNeedlesInHay(needlesStr, hay);
    console.log(needlesInHay);

    const transformHayArrayToHtmlReducer = (acc, curr, index) => {
      return acc   (index % 2 === 0 ? curr : `<b>${curr}</b>`);
    };

    return needlesInHay.reduce(transformHayArrayToHtmlReducer);
  };
  
  const result1 = highlightNeedlesInHay("Foo bar", "I pit the fool");
  console.log(result1);
  document.write(result1   "<br />");
  
  const result2 = highlightNeedlesInHay("Foo bar", "I pit the bar fool");
  console.log(result2);
  document.write(result2   "<br />");
  
  const result3 = highlightNeedlesInHay("Barbeque bar", "Quality barbeque grill");
  console.log(result3);
  document.write(result3   "<br />");
</script>