0

I have a piece of code, which goal is to replace every occurence of @something with an anchor tag. The replacement itself works fine, the looping in general does not. Just consider this a string manipulation method, which has to find every occurence of something, then replace it with something else - but can't do replace all, since it depends on what comes after the '@' character.

Here is the code.

private generateAnchors(content: string) {
    let cutIndex = 0;
    let contentToReturn = content;
    let leftToAnalyze = content;
    if (leftToAnalyze.indexOf('@') !== -1) {
        while (true) {
            leftToAnalyze = content.substring(cutIndex);
            leftToAnalyze = leftToAnalyze.substring(content.indexOf('@'));
            let tag = leftToAnalyze.substring(leftToAnalyze.indexOf('@'), leftToAnalyze.indexOf(' ', leftToAnalyze.indexOf('@')));
            cutIndex += leftToAnalyze.indexOf(tag)+tag.length;
            let address = tag.substring(1, tag.length);
            let anchor = '<a href="/home/user/'+address+'">'+tag+'</a>';
            let newContentSubString = leftToAnalyze.replace(tag, anchor);
            contentToReturn = contentToReturn.replace(leftToAnalyze, newContentSubString);
            if (leftToAnalyze.indexOf('@') === -1) break;
        }
    }
    return contentToReturn;
}

in a small string like ' hello @JEDS & @Mill also @theHulk deserves a mention' it works fine. However I found an occurrence with was a larger string, where the @ tag was in the end of the string, and it seemed like it was looping forever, and replacing stuff it weren't supposed too.

What am I overseeing in this piece of code?

1 Answer 1

1

There's a bug in your cutIndex calculation. It looks up the index of tag in leftToAnalyze and uses that index into content at the beginning of the loop (i.e., leftToAnalyze = content.substring(cutIndex)), causing it to analyze the same text from the previous run.

cutIndex should actually be looking in content:

// cutIndex += leftToAnalyze.indexOf(tag)+tag.length;  // DON'T DO THIS
cutIndex += content.indexOf(tag)+tag.length;

In addition, the loop should bail early if leftToAnalyze is empty:

while (true) {
  leftToAnalyze = content.substring(cutIndex);
  const indexOfFirstAt = content.indexOf('@');
  leftToAnalyze = leftToAnalyze.substring(indexOfFirstAt);

  // nothing left? bail
  if (!leftToAnalyze) break;
  ...
}

function generateAnchors(content) {
  let cutIndex = 0;
  let contentToReturn = content;
  let leftToAnalyze = content;
  if (leftToAnalyze.indexOf('@') !== -1) {
      let limit = 100;
      let i = 0;
      while (i++ < limit) {
          leftToAnalyze = content.substring(cutIndex);
          const indexOfFirstAt = content.indexOf('@');
          leftToAnalyze = leftToAnalyze.substring(indexOfFirstAt);
          if (!leftToAnalyze) break;
          const indexOfNextAt = leftToAnalyze.indexOf('@');
          const indexOfSpace = leftToAnalyze.indexOf(' ', indexOfNextAt);
          let tag = leftToAnalyze.substring(leftToAnalyze.indexOf('@'), indexOfSpace);
          cutIndex += content.indexOf(tag)+tag.length;
          let address = tag.substring(1, tag.length);
          let anchor = '<a href="/home/user/'+address+'">'+tag+'</a>';
          let newContentSubString = leftToAnalyze.replace(tag, anchor);
          contentToReturn = contentToReturn.replace(leftToAnalyze, newContentSubString);
          if (leftToAnalyze.indexOf('@') === -1) break;
      }
  }
  return contentToReturn;
}

const input = 'foo @bar baz @qux @ @@@@@';

const output = generateAnchors(input);
console.log(output);

But I recommend simplifying your code with regular expressions. This loop could actually be simplified into one line with String#replace:

return content.replace(/(@([^ @]+))/ig, '<a href="/home/user/$2">$1</a>');

function generateAnchors(content) {
  return content.replace(/(@([^ @]+))/ig, '<a href="/home/user/$2">$1</a>');
}

const input = 'foo @bar baz @qux @ @@@@@';

const output = generateAnchors(input);
console.log(output);

Explanation of /(@([^ @]+))/ig

Sign up to request clarification or add additional context in comments.

3 Comments

acutally this returns 'Hello <a href="/home/user/@something>@something</a> fs' where is should return Hello <a href="/home/user/something">@something</a> fs'. the @ Tag need to be removed, and there is a missing "
Adding the " is easily done with return content.replace(/(@[^ @]+)/ig, '<a href="/home/user/$1">$1</a>'); but how do I remove the @ at the first occurrence? ($1)
@JonasPraem Ok, fixed. You'd use a second capture group to capture only the name. See updated answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.