-2

Let's say I have a sentence and an array of words:

let sentence = 'My * is * years old.';
let words = ['dog', 3];

//expected result => My dog is 9 years old.

How would I replace each asterisk (*) with the appropriate word in the given array? But let's also say that there's case where there are more asterisks than elements in the array:

let sentence = 'The soldiers marched *, *, *, *.';
let words = ['left', 'right'];

//expected result => The soldiers marched left, right, left, right.

Is using regex the only way to solve this or is there a vanilla JS solution?

Jared Smith
  • 19,721
  • 5
  • 45
  • 83
Chris
  • 21
  • 3
    Have you tried anything at all? Yes it is possible with vanilla js. – basic Feb 17 '19 at 21:24
  • Regex is built in to js, so it is vanilla js. What solution have you come up with that uses regex? It may be possible to repurpose it so that it doesn't use regex. – Khauri Feb 17 '19 at 21:30
  • In the future, please do not add tags to questions that don't fit your question. I've removed the functional programming tag, as this has nothing to do with functional programming. You can hover over a tag to get a short description of what it's about. Understand that many regular users of the site follow certain tags, and we can get a little grumpy about questions popping up that are not germane to our interests. Secondly, and much less importantly, regexes *are* vanilla js :) – Jared Smith Feb 18 '19 at 12:53

5 Answers5

3

You could take a replacement function with an additional value for the starting index, which has a default value of zero.

For keeping the index in a valid range, you could take the remainder operator % with the length of the array.

const
    replace = (string, array, i = 0) =>
        string.replace(/\*/g, _=> array[i++ % array.length]);

console.log(replace('My * is * years old.', ['dog', 3]));
console.log(replace('The soldiers marched *, *, *, *.', ['left', 'right']));
Nick
  • 138,499
  • 22
  • 57
  • 95
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • 1
    i was thinking of a start with an offset for the index. (beside the declaring and initializing of a necessary variable.) – Nina Scholz Feb 17 '19 at 21:38
0

let sentence = 'My * is * years old.';
let words = ['dog', 3];
let count =0;
while (sentence.indexOf('*') > -1) {
    sentence = sentence.replace('*', words[count++]);
    if (count >= words.length) count = 0;
}
console.log(sentence);

shows: "My dog is 3 years old."

Majid Laissi
  • 19,188
  • 19
  • 68
  • 105
0

If you really dislike regular expressions you could do this:

words = ["left", "right"];
"The soldiers marched *, *, *, *.".split("*").map(function (x, i) {
  return i === 0 ? x : words[(i + 1) % words.length] + x;
}).join("");

Trace of execution:

init  | "The soldiers marched *, *, *, *."
split | ["The soldiers marched ", ", ", ", ", ", ", "."]
i = 0 | ["The soldiers marched ", ", ", ", ", ", ", "."]
i = 1 | ["The soldiers marched ", "left, ", ", ", ", ", "."]
i = 2 | ["The soldiers marched ", "left, ", "right, ", ", ", "."]
i = 3 | ["The soldiers marched ", "left, ", "right, ", "left, ", "."]
i = 4 | ["The soldiers marched ", "left, ", "right, ", "left, ", "right."]
join  | "The soldiers marched left, right, left, right."
0

Another regexp free option:

s = "The soldiers marched *, *, *, *.";
f = Function("words", "var i = 0; return \"" + s.split("*").join(
  "\" + words[(i++) % words.length] + \""
) + "\";");
> | f(["L", "R"])
< | "The soldiers marched L, R, L, R."
> | f(["L", "R"].reverse())
< | "The soldiers marched R, L, R, L."
> | f(["L", "R", "R"])
< | "The soldiers marched L, R, R, L."

Beware of malicious code though:

s = "The soldiers marched *, *, *, *.";
s = "\", console.log(\"VIRUS ATTACK!!! CALL +XX-XXXXX-XXXXX NOW!!!\"), \"" + s;
f = Function("words", "var i = 0; return \"" + s.split("*").join(
  "\" + words[(i++) % words.length] + \""
) + "\";");
> | f(["L", "R"])
  | VIRUS ATTACK!!! CALL +XX-XXXXX-XXXXX NOW!!!
< | "The soldiers marched L, R, L, R."

You should always sanitize the input:

s = "The soldiers marched *, *, *, *.";
s = "\", alert(\"VIRUS ATTACK!!! CALL +XX-XXXXX-XXXXX NOW!!!\"), \"" + s;
s = s.split("\"").join("\\\""); // potentially not sufficient!
f = Function("words", "var i = 0; return \"" + s.split("*").join(
  "\" + words[(i++) % words.length] + \""
) + "\";");
> | f(["L", "R"])
< | "", alert("VIRUS ATTACK!!! CALL +XX-XXXXX-XXXXX NOW!!!"), "The soldiers marched L, R, L, R."

But I would not venture that it's bullet proof :-|

0

One character after the other:

input = "The soldiers marched *, *, *, *.";
words = ["left", "right"];
output= ""
for (i = 0, j = 0; i < input.length; i++) {
  output += input[i] !== "*" ? input[i] : (
    words[(j++) % words.length]
  );
}
console.log(output)

As you can see, there are multiple alternatives to regular expressions. Anyway, what you really need to understand is the remainder operator (%):

0 % 2 = 0 | 0 -> 0
1 % 2 = 1 | 1 -> 1
2 % 2 = 0 | 2 -> 0
3 % 2 = 1 | 3 -> 1
4 % 2 = 0 | 4 -> 0

0, 1, 0, 1, 0, 1, ... Got it? The result never reaches the right operand. This is especially useful when you need to iterate over the same array more than once:

abc = ["A", "B", "C"]
for (i = 0; i < 2 * abc.length; i++) {
  // j will never reach abc.length
  j = i % abc.length; // 0, 1, 2, 0, ...
  console.log(i, j, abc[j]);
}