Reputation: 2444
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?
Upvotes: 0
Views: 302
Reputation: 138326
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
Upvotes: 1