Реагируйте: выделите текст между двумя индексами - PullRequest
0 голосов
/ 26 апреля 2020

Таким образом, для каждого прогноза API автозаполнения Google google также возвращает подходящие подстроки для каждой.

Ввод: Сан 68

Прогноз: Сан-Франциско 68

Подходящие подстроки: [{ offset: 0, length: 3 }, { offset: 15, length: 2 }]

Ожидание: Сан Франциско 68

Моя цель состоит в том, чтобы выделить части прогноза, используя соответствующие подстроки. Теперь есть несколько проблем. Я мог бы использовать функцию replace и заменить каждую подстроку на <b>str</b>, но она возвращает строку, что означает, что, если я не использую dangerouslySetInnerHTML, этот метод не работает.

Я также не думаю, что есть способ заменить несколько подстрок. Я пытался использовать функцию reduce, но после первого l oop это не сработало бы, потому что индексы были бы неправильными.

const highlight = (text, matched_substrings) => {
  return matched_substrings.reduce((acc, cur) => {
    return acc.replace(
      acc.substring(cur.offset, cur.length),
      (str) => `<b>${str}</b>`
    )
  }, text)
}

Так есть ли способ сделать это? Я думаю, что React делает это более сложным.

Ответы [ 2 ]

1 голос
/ 26 апреля 2020

, вероятно, не лучшее решение, но однозначно работает :) Необходимым условием является то, что массив matched_substrigs должен быть отсортирован по смещению

export const highlightText = (text, matched_substring, start, end) => {
    const highlightTextStart = matched_substring.offset;
    const highlightTextEnd = highlightTextStart + matched_substring.length;

    // The part before matched text
    const beforeText = text.slice(start, highlightTextStart);

    // Matched text
    const highlightedText = text.slice(highlightTextStart, highlightTextEnd);

    // Part after matched text
    // Till the end of text, or till next matched text
    const afterText = text.slice(highlightTextEnd, end || text.length);

    // Return in array of JSX elements
    return [beforeText, <strong>{highlightedText}</strong>, afterText];
};

export const highlight = (text, matched_substrings) => {
    const returnText = [];

    // Just iterate through all matches
    for (let i = 0; i < matched_substrings.length; i++) {
        const startOfNext = matched_substrings[i + 1]?.offset;
        if (i === 0) { // If its first match, we start from first character => start at index 0
            returnText.push(highlightText(text, matched_substrings[i], 0, startOfNext))
        } else { // If its not first match, we start from match.offset 
            returnText.push(highlightText(text, matched_substrings[i], matched_substrings[i].offset, startOfNext))
        }
    }

    return returnText.map((text, i) => <React.Fragment key={i}>{text}</React.Fragment>)
};
0 голосов
/ 26 апреля 2020

вот решение, если вы не возражаете против использования dangerouslySetInnerHTML

const Highlight = ({ text, substrings }) => {
  const html = substrings.reduce((acc, cur, idx) => {
    return acc.replace(
      new RegExp(
        '(?<=^.{' + (cur.offset + idx * 17) + '})(.{' + cur.length + '})',
      ),
      (str) => `<strong>${str}</strong>`,
    )
  }, text)
  return <span dangerouslySetInnerHTML={{ __html: html }} />
}

используйте его вот так

<Highlight text={...} substrings={...} />
...