String.prototype.replaceAll - a String.prototype for replacing multiple characters with different characters

String.prototype.replaceAll - a String.prototype for replacing multiple characters with different characters
0.0 0

#1

SPOILER ALERT ! This post will contain spoilers for solving the challenge “Convert HTML Entities”, so only read on if you have completed this challenge.

So today I was working on refactoring some challenges which I had already solved. When I re-looked at my “Convert HTML Entities” solution (below), I was like, what if there were a hundred different replacements? I did not like the thought of putting in all of the .replace statements in my code.

// original solution
function convertHTML(str) {
  return str.replace(/&/g,"&")
    .replace(/</g,"&lt;")
      .replace(/>/g,"&gt;")
      .replace(/"/g,"&quot;")
        .replace(/'/g,"&apos;");
}

It got me to thinking about creating a new String prototype called replaceAll which would accept one parameter (conversionsObj) which would contain a mapping of all the characters needing replacements along with their corresponding replacement value.

I came up with two solutions. The first uses a regular expression and reduce to produce the “replaced” string and the second splits up the string into an array, so I could use map to iterate over the string and make each conversion before rejoining the array back into a string. One would simply need to declare an object with the necessary conversions and a string to call the function on (example below):

const conversions = {'&':'&amp;', '<':'&lt;','>':'&gt;','"':'&quot;',"'":'&apos;'};
const str = 'Dolce & Gabbana';
str.replaceAll(conversions); // results in 'Dolce &amp; Gabbana'

I think the second is more readable, but I will need to do some testing to validate which version performs the best.

Prototype #1 (using regex expression):

// regex solution
String.prototype.replaceAll = function(conversionsObj) {
 return Object.keys(conversions).reduce((newStr,find) => newStr.replace(new RegExp(find, 'g'),conversions[find]),this);
};

Prototype #2 (using map):

// Array.prototype.map solutions
String.prototype.replaceAll = function(conversionsObj) {
 return this.split('').map(char => conversionsObj[char] ? char = conversionsObj[char] : char).join('');
};

#2

In case anyone is interested in the test results comparing the two versions of my String.prototype.replaceAll, I have posted them below. I ran both versions with multiple number of characters (500,5K, 50K, 500K, and 5 million) in the string. I calculated the average execution time over 100 reps of each number of characters in the string. Also, to see the two most extreme cases, I first generated strings with only the characters which needed replacement (i.e &, <, >, ", ') and then a different run of strings without any characters needing replacement (I used the letter ‘a’ for all the characters in the string).

My conclusions are as follows:

  • The RegExp version was by far the fastest version no matter what size the string was and whether or not there were replacements made.

  • When comparing the strings where all characters needed replacement to the strings where no characters needed replacement, there was no significant difference in the Map version, but the RegExp version was significantly faster when no replacements were needed.

Test results for when every character in string needed a replacement

Number of string characters = 500
RegExp version: Average execution (100 reps) is 0.01ms
Map version: Average execution (100 reps) is 0.20ms

Number of string characters = 5,000
RegExp version: Average execution (100 reps) is 0.11ms
Map version: Average execution (100 reps) is 2.03ms

Number of string characters = 50,000
RegExp version: Average execution (100 reps) is 1.08ms
Map version: Average execution (100 reps) is 23.89ms

Number of string characters = 500,000
RegExp version: Average execution (100 reps) is 14.77ms
Map version: Average execution (100 reps) is 258.35ms

Number of string characters = 5,000,000
RegExp version: Average execution (100 reps) is 129.70ms
Map version: Average execution (100 reps) is 2729.01ms

Test results for when no character in string needed a replacement

Number of string characters = 500
RegExp version: Average execution (100 reps) is 0.00ms
Map version: Average execution (100 reps) is 0.19ms

Number of string characters = 5,000
RegExp version: Average execution (100 reps) is 0.01ms
Map version: Average execution (100 reps) is 1.88ms

Number of string characters = 50,000
RegExp version: Average execution (100 reps) is 0.07ms
Map version: Average execution (100 reps) is 25.56ms

Number of string characters = 500,000
RegExp version: Average execution (100 reps) is 0.69ms
Map version: Average execution (100 reps) is 233.94ms

Number of string characters = 5,000,000
RegExp version: Average execution (100 reps) is 7.00ms
Map version: Average execution (100 reps) is 2407.60ms

NOTE; If anyone is interested in the script I used to generate these results, just let me know.