1

So I have this code:

var input = document.querySelector("input");
var h1 = document.querySelector("h1");

input.addEventListener("input", function(e){
  h1.innerText = input.value.replace(/[a]/gi, 'e').replace(/[e]/gi, 'i').replace(/[i]/gi, 'o').replace(/[o]/gi, 'u').replace(/[u]/gi, 'y')
});
<input type="text">
<h1></h1>

As you can see, if I press "a" it will replace "a" with "e". But then it's like a domino effect: it now detects "e" and will replace it with "i", and so on until it gets to "y". How do I prevent this?

Note: I'm not that good at regex so please try to explain.

MWR
  • 304
  • 1
  • 2
  • 12
  • 1
    Swap their order – Asons Aug 26 '18 at 19:20
  • That doesn't make any sense. If I swapped the order it would still come back to the last letter change, and end up just like in my case, at letter "y". – MWR Aug 26 '18 at 19:22
  • Which `.split("").reverse().join("")`? ... there is no in the question – Asons Aug 26 '18 at 19:26
  • Yeah sorry, I also have that in my project, and I didn't include it here but my brain thought I did... pretty much brainfart – MWR Aug 26 '18 at 19:26

3 Answers3

2

If you swap the replace() chain it will work, and instead start with .replace(/[u]/gi, 'y') and end with .replace(/[a]/gi, 'e')

Stack snippet

var input = document.querySelector("input");
var h1 = document.querySelector("h1");

input.addEventListener("input", function(e){
  h1.innerText = input.value.replace(/[u]/gi, 'y').replace(/[o]/gi, 'u').replace(/[i]/gi, 'o').replace(/[e]/gi, 'i').replace(/[a]/gi, 'e')
});
<input type="text">
<h1></h1>
Asons
  • 84,923
  • 12
  • 110
  • 165
  • 1
    Ohhh! I understand it now. The order I put them in when I made this was just like in a chain reaction. – MWR Aug 26 '18 at 19:32
  • @MWR Yepp...been doing that myself many times :) – Asons Aug 26 '18 at 19:33
  • Hmm, it's almost perfect. I have one more question. If I replace the "y"s with "a", how would I do it, so it's not in a chain? – MWR Aug 26 '18 at 19:35
  • 1
    @MWR That is not possible with a chain of `replace()` as you will get a circular reference. Roko posted an answer that will cover that here: https://stackoverflow.com/a/52029652/2827823 – Asons Aug 26 '18 at 19:42
2

Instead of using endless .replace() chains,

  • Create a substitutions library
  • Join lib Keys into a query RegExp /(a|e|i|o|u)/
  • Use .replace() once for fun and profit

var input = document.querySelector("input");
var h1 = document.querySelector("h1");


var lib = {
  'a':'e',
  'e':'i',
  'i':'o',
  'o':'u',
  'u':'y',
};


input.addEventListener("input", function(e){

  var q = new RegExp("("+ Object.keys(lib).join('|') +")", "ig");
  h1.textContent = this.value.trim().replace(q, $1 => lib[$1]);
  
});
<input type="text">
<h1></h1>

How it works:

The String.prototype.replace() method offers a callback function, where inside it's arguments you can provide the aliases to the regexp matches () ← Match Group. We're interested in only the first-and-only group, used as $1.
Inside the callback we replace the matched character occurrence with the one from our substitutions library.

To make it more understandable, here's the expanded version:

//...

input.addEventListener("input", function(e){

  var characterKeys = Object.keys(lib).join('|'); // "a|e|i|o|u"
  var matchGroup = "("+ characterKeys  +")";      // "(a|e|i|o|u)"
  var reg = new RegExp(matchGroup , "ig");
  // meaning: Match any of the characters present in the group
  // (the | is the options delimiter).

  // Finally: 
  var inputVal = this.value.trim();
  var replacedText = inputVal.replace( reg , function(match1) {
     return lib[ match1 ];
  });

  h1.textContent = replacedText;

});

What the return lib[ match1 ] does is simply:

If while regex-parsing the string, the "e" character is encountered, return it's library replacement, in this case lib[ "e" ] === "i" therefore the character "i" gets inserted at that callback point.

Also, get to know Object/keys

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • Huh, what does this line of code do? I've only seen something like that in PHP(inside `.replace`). `.replace(q, $1 => lib[$1]);` – MWR Aug 26 '18 at 19:42
  • @MWR I'll explain soon while expanding my answer – Roko C. Buljan Aug 26 '18 at 19:44
  • Okay, thanks for helping! – MWR Aug 26 '18 at 19:45
  • 1
    wow ! You just opened up a new door for me an regexes ! Also, this solution has the superior value also by working even if the replacement rules are circular, i.e. I just tried to add `'y': 'a'`, to the query, and it works perfectly as well. – Pac0 Aug 26 '18 at 19:46
  • I did that as well Pac0, and it worked, though I still don't understand why he created an object for this, I'll wait for his expalanation. – MWR Aug 26 '18 at 19:50
  • I'll study it in-depth later as it's night here and I'm super tired. I made out some of it but not everything. I still upvoted because I know you explained it well. – MWR Aug 26 '18 at 20:05
  • 1
    @MWR And if the need for it to work in IE, do `h1.textContent = this.value.trim().replace(q, function(m) { return lib[m]; });` – Asons Aug 26 '18 at 20:32
1

you need to reverse the order of your replacement.

Since you have an end (the y's don't get replaced), you can first replace the u's by y's, the the o's by u's and so on.

Pac0
  • 21,465
  • 8
  • 65
  • 74