0

Yes, I want a character "translate" function in javascript like that in php. I made the following, but it is ghastly. Surely there must be a better way -- using regular expressions?

<html>
<head>
<script>
window.onload = function() { 
  "use strict";
  console.log(translate("abcdefg", "bdf", "XYZ")); // gives aXcYeZg -=-=-
}
function translate(v1, xlatfrom, xlatto) {
  var ch, ipos, retstr = "";
  if (xlatfrom.length != xlatto.length) return ""; // lengths must be =
  for (var i1=0; i1<v1.length; i1+=1) { // go through string
    ch = v1.substring(i1, i1+1); // character by character
    ipos = xlatfrom.indexOf(ch); // ck if in xlatfrom
    if (ipos >= 0) ch = xlatto.substring(ipos, ipos+1); // if yes, replace
    retstr += ch; } // build up return string
  return retstr;
}
</script>
</head>
<body>
</body>
</html>

EDIT: I've accepted the @dani-sc answer. I'm not going to pursue performance. But it's so DIDACTIC! And thanks for the "spread operator" info. Here's how I might use his answer:

function translate(v1, xlatfrom, xlatto) { // like the PHP translate
 var mapobj = strsToObject(xlatfrom, xlatto); // make str1.ch's:str2ch's object
 return [...v1].map(ch => mapobj[ch] || ch).join(''); // ... is js "spread operator"
}
function strsToObject(str1, str2) { // make object from strings
  if (str1.length != str2.length) return {}; // lengths must be =
  var retobj = {};
  for (var i1=0; i1<str1.length; i1+=1) { // just str[i1]: str2[i1]
    retobj[str1.substring(i1, i1+1)] = str2.substring(i1, i1+1); }
  return retobj;
}

or (this is GREAT! THANKS!)

function translate(v1, xlatfrom, xlatto) { // like the PHP translate
  if (xlatfrom.length != xlatto.length) return ""; // lengths must be =
  var mapobj = {}; // make object for mapping
  for (var i1=0; i1<xlatfrom.length; i1+=1) { // just str[i1]: str2[i1]
    mapobj[xlatfrom.substring(i1, i1+1)] = xlatto.substring(i1, i1+1); }
  return [...v1].map(ch => mapobj[ch] || ch).join(''); // ... is js "spread operator"
}
dcromley
  • 1,373
  • 1
  • 8
  • 23
  • 1
    Something like the answers in [this question](https://stackoverflow.com/q/10726638/1810460) might be what you're looking for – Hamms May 07 '18 at 21:53
  • @Hamms Thanks, my searches didn't hit "Perl tr function". Lots in there. – dcromley May 08 '18 at 01:38

2 Answers2

2

Well, if you want, you could use regular expressions like this:

function translate(input, oldCharacters, newCharacters) {
  let output = input;
  const oldChArr = [...oldCharacters];
  const newChArr = [...newCharacters];
  for (let i = 0; i < oldChArr.length; i += 1) {
    output = output.replace(new RegExp(oldChArr[i], 'g'), newChArr[i]);
  }
  return output;
}

function translateFixed(input, replacements) {
  return input.replace(/./g, ch => replacements[ch] || ch);
}

function translateFixedNoRegEx(input, replacements) {
  return [...input].map(ch => replacements[ch] || ch).join('');
}

console.log(translate("abcdefgbdb", "bdf", "XYZ"));
console.log(translate("abcdefg", "cde", "dec"));
console.log(translateFixed("abcdefg", {c: 'd', d: 'e', e: 'c'}));
console.log(translateFixedNoRegEx("abcdefg", {c: 'd', d: 'e', e: 'c'}));

If you would be okay with changing the method's signature, it could be made a bit more concise of course.

Edit: I've added two more methods which actually achieve what you're looking for. Just for reference, I left the original method translate in there as well.

translateFixed uses regular expressions to match every single character and replace it if it was specified in the replacements parameter. translateFixedNoRegex just creates an array of characters out of the input string and iterates over them. If the character ch matches one in the replacements parameter, it's replaced, otherwise it's left unchanged. Afterwards, we'll convert it back to a string by concatenating the characters.

You asked about [...array]: It's the spread operator, introduced with ES6. When used on a string, it just takes every character and puts it as a single entry into an array. That means, these both lines are equivalent:

console.log([..."mystring"]);
console.log("mystring".split(''));
dani-sc
  • 308
  • 1
  • 8
  • 1
    Please say something about the "..." in "[...oldCharacters]". I'd like to know about this. A problem with your function -- "translate("abcdefg", "cde", "dec")" gives "abcccfg" instead of "abdecfg", which is what I would want. I'm going to look more at this. Thanks. – dcromley May 08 '18 at 01:10
  • 1
    @dcromley Hi, I've edited my answer with two new functions and an explanation on [...array] – dani-sc May 08 '18 at 10:03
  • @Bergi I've checked my routines and dani-sc 's and there is NO COLLUSION! – dcromley May 09 '18 at 16:16
-1

function translate(val, xlatfrom, xlatto) { // 
  if (xlatfrom.length !== xlatto.length) return "";
  
  Array.from(xlatfrom).forEach((key, index) =>  {
    val = val.replace(key, xlatto[index]);
  })
  return val;
}

console.log(translate("abcdefg", "bdf", "XYZ"));
Zohaib Ijaz
  • 21,926
  • 7
  • 38
  • 60
  • 1
    This suffers from the (I think) same problem as from @dani-sc, "translate("abcdefg", "cde", "dxx")" gives "abxdxfg" instead of "abdxxfg", which is what I would want. I'm going to look more at this. Thanks. – dcromley May 08 '18 at 01:18
  • That doesn't work if the letters in `xlatfrom` and `xlatto` collide – Bergi May 08 '18 at 09:58
  • @Bergi Complete specs for this function are not mentioned. So one option left for me to loop into php translate() code and convert into javascript. I don't have that much time :) – Zohaib Ijaz May 08 '18 at 11:10