У меня есть строка "привет @ адрес электронной почты, которую вы запросили test@test.com для пользователя @test"

Я хочу превратить это в:

['hello ', <a href="">@steph</a>, 'the email you requested is test@test.com for user ', <a href="">@test</a>];

Вот что у меня есть:

function matchUserMention(text) {
    var pattern = /\B@[a-z0-9_-]+/gi;
    return text.match(pattern); // [@steph, @test]
}

function applyUser(string) {
    let text = string;
    if (typeof text != 'string') return text;
    var arr = [];

    function replaceAll(str, find, replace) {
        return text.replace(new RegExp(find, 'g'), replace);
    }
    const matches = matchUserMention(text);
    _.each(matches, (match) => {
        text = replaceAll(text, match, <a href={'https://test.co'}>${match}</a>);
    });
    return text; 
}

Текст прямо сейчас возвращает:

'hello <a href="">@steph</a>, the email you requested is test@test.com for user <a href="">@test</a>

Кто-нибудь знает лучший способ создания массива отсюда? Это не может быть слишком тяжелым, так как мне нужно, чтобы это вызывали постоянно

0
ReganPerkins 10 Янв 2017 в 23:20

4 ответа

Лучший ответ

Этот подход сопоставляет имена пользователей и использует возможность RegExp запоминания последнего положения матча. Поэтому последующий вызов exec() будет соответствовать последующим именам пользователей. Объект соответствия, возвращаемый exec(), является массивом, содержащим совпадающие тексты и атрибут index, указывающий на позицию соответствия.

Код копирует текст из последней позиции (изначально позиция 0) в соответствующее имя пользователя, затем добавляет «завернутое» имя пользователя и начинается заново, пока не останется совпадений с именем пользователя.

Код комментируется построчно, описывая его функцию:

function wrapUsernames(str) {
        // match "usernames" ("at" followed by text, preceeded by non-word character or line-start), 
        // the RegExp keeps its state
    var re = /(?:([^\w]|^)(@[a-z]+))/g,
        // store a RegExp match object
        match,
        // store the text fragments
        results = [],
        // remember last index for substring copy
        lastIndex = 0;

    // match and store result, returns null if it does no longer match
    while (match = re.exec(str)) {
        // copy text from last match / start to username 
        // (only if not matched at index 0 to prevent empty string)
        if (match.index != 0) {
            results.push( str.substring(lastIndex, match.index) );
        }
        if (match[1].length >= 1) {
            // it also matched the char before the username, append it
            results.push(match[1]);
        }
        // copy matched username and wrap in a tag
        results.push('<a href="...">' + match[2] + '</a>');
        // update the index to start copy at the next position
        lastIndex = match.index + match[0].length;
    }

    // append the remaining string (only if it wouldn't be an empty string)
    if (lastIndex < str.length) {
        results.push(str.substring(lastIndex));
    }

    return results;
}

Это также должно соответствовать именам пользователей с префиксом, отличным от пробела:

> wrapUsernames("(@steph) the email you requested is test@test.com for user (@test)")
< Array [ "(", "<a href="...">@steph</a>", ") the email you requested is test@test.com for user ", "(", "<a href="...">@test</a>", ")" ]
> wrapUsernames("@steph the email you requested is test@test.com for user (@test)")
< Array [ "<a href="...">@steph</a>", " the email you requested is test@test.com for user ", "(", "<a href="...">@test</a>", ")" ]
> wrapUsernames("hi, @steph the email you requested is test@test.com for user @test")
< Array [ "hi,", " ", "<a href="...">@steph</a>", " the email you requested is test@test.com for user ", " ", "<a href="...">@test</a>" ]
0
try-catch-finally 11 Янв 2017 в 06:42

Спасибо всем, это закончилось тем, что я использовал на случай, если кто-нибудь еще столкнется с этим. Это смесь других ответов.

 function applyUser(str) {
        var arr = [],
            userRegEx = /\B@[a-z0-9_-]+/gi,
            execResult,
            lastFoundLen = 0,
            found,
            strLen = str.length,
            index = 0;
        while ((execResult = userRegEx.exec(str)) !== null) {
            const newIndex = execResult.index;
            if (newIndex > index + lastFoundLen) arr.push(str.substring(index + lastFoundLen, newIndex));
            found = execResult[0];
            if (!found) break;
            const foundLen = found.length;
            const userId = execResult[0].substring(1);
            arr.push(
                <ProfilePersona
                    key={userId}
                    noAvatar
                    view="compact"
                    userId={userId}>
                    {userId}
                </ProfilePersona>
            );

            index = newIndex;
            lastFoundLen = foundLen;
        }
        if (index + lastFoundLen < strLen) arr.push(str.substr(index + lastFoundLen));
        if (!arr.length) arr.push(str);
        return arr;
    }
0
ReganPerkins 13 Янв 2017 в 18:27
function applyUser(string) {
  const reg = /\B@[a-z0-9_-]+/ig;
  const res = [];
  let match;

  // loop while we can match something
  while((match = reg.exec(string)) !== null) {
    // push to result array portion of string before user match, if any
    if (match.index > 0) {
      res.push(string.substring(0, match.index));
    }

    // push updated user name to result array
    res.push('<a href="">' + match[0] + '</a>');

    // remove processed part from a string
    string = string.substr(match.index + match[0].length);
  }

  // push any reminder to the result array
  if (string) {
    res.push(string);
  }
  return res;
}
1
Marian Javorka 10 Янв 2017 в 21:21

Вы можете использовать split с регулярным выражением, имеющим группу захвата. Это приведет к массиву, который имеет совпадающие элементы по нечетным индексам. Затем вы можете применить обертку <a> к ним через .map(). Наконец, вы можете использовать .filter() для удаления пустых строк (например, в начале или конце массива):

// Sample data
var url = 'https://elk.co';
var string = "hello @steph the email you requested is test@test.com for user @test";

// The transformation
var parts = string.split(/(\B@[\w-]+)/g)
                  .map( (w,i) => i%2 ? `<a href='${url}'>${w}</a>` : w )
                  .filter(Boolean);

// Output the result
console.log(parts);
1
trincot 11 Янв 2017 в 07:53