It's easy to write a function that does it for a single range. Write that function, then use reduce
to do it repeatedly for each range. In this case, you want to make sure you do the end ranges first so it doesn't change the offset for prior ranges.
If you make sure you go through the ranges in reverse order (sort the array if necessary, or use reduceRight
if they're already ordered), then it can be this simple:
// Wraps one range
function wrap(str, [i, j]) {
const beg = str.substring(0, i),
mid = str.substring(i, j),
end = str.substring(j);
return `${beg}<b>${mid}</b>${end}`;
}
[[2, 5], [11, 14]].reduceRight(wrap, 'brave new world')
So I'd write magic like this:
function magic(str, ranges, b='<bold>', a='</bold>') {
const wrap = (str, [i, j]) => str.substring(0, i) + b +
str.substring(i, j) + a + str.substring(j);
return ranges.sort(([i], [j]) => j - i).reduce(wrap, str);
}