1

I have a function to change English numeric chars to Persian:

  function en2fa(str){
    string = str.replace('1', '۱');
    string = string.replace('2', '۲');
    string = string.replace('3', '۳');
    string = string.replace('4', '۴');
    string = string.replace('5', '۵');
    string = string.replace('6', '۶');
    string = string.replace('7', '۷');
    string = string.replace('8', '۸');
    string = string.replace('9', '۹');
    string = string.replace('0', '۰');

    return string;
  }

And I want to know how js will do turns out in-depth:

var a = '12345';
alert(en2fa(a.replace('1', '3')));

I give this:

۳۲3۴۵

Why? How js parse this text?

============

Edited: I have mistake in function code. en2fa() edited.

Chalist
  • 3,160
  • 5
  • 39
  • 68
  • 1
    Can you show us the implementation of `en2fa(..)`? You posted `fa2en(..)`. – gregor Jun 16 '13 at 12:41
  • 1
    The title of this question is totally unrelated to the content. Future visitors to this site with the same problem will never find this question, and visitors with questions about indentation will see this question and waste their time. – Raymond Chen Jun 16 '13 at 13:01
  • Please clarify the content of `en2fa` (you're not showing it, only `fa2en`) – Sebas Jun 16 '13 at 13:07

2 Answers2

5

From clues in your question, Your issue is in the way you are doing the replace. You showed us fa2en, I presume your en2fa is similar. However a better and working implementation would be as follows :

  function en2fa (str) {
    return str.replace (/\d/g, function (d) {
      return '۰۱۲۳۴۵۶۷۸۹'.charAt (+d);
    });
  }

You main problem was as described by @tom in your code using string replacement only replaces the first occurrence. @tom 's answer while it might work is not standard. To replace all occurrences you should use a regExp replace with a g modifier. This results in significantly shorter code also !

HBP
  • 15,685
  • 6
  • 28
  • 34
  • +1 for the obviously most elegant solution - and *probably* most efficient one as well. – Sebas Jun 16 '13 at 13:15
  • @Sebas Interestingly, using an object instead of a string doubles the performance: http://jsperf.com/persian-translation – tom Jun 16 '13 at 16:01
  • @tom, cool! That's why I insisted on the "probably" :-) Actually regular expressions are known to not be the fastest replace method.. – Sebas Jun 16 '13 at 16:57
  • @Sebas On Chrome regex is indeed much slower that for-loop, but in Firefox it's the other way round. Very interesting. – tom Jun 16 '13 at 17:05
  • @hbp thanks, but your answer is just for en2fa digits. It's not popular way to replace any chars with anothers (I think). but for en2fa is perfect :) thanks – Chalist Jun 17 '13 at 05:21
2

By default, replace only replaces the first occurrence. To replace all occurrences use a regular expression with the g flag:

function en2fa(str){
    string = string.replace(/1/g, '۱');
    string = string.replace(/2/g, '۲');
    // ...
    return string;
}

var a = '12345';
alert(en2fa(a.replace(/1/g, '3')));

You can make the translation more concise using a lookup table:

var en2faDict = {};
var fa2enDict = {};
"۰۱۲۳۴۵۶۷۸۹".split('').forEach(function(fa, en) {
    en = "" + en;
    en2faDict[en] = fa;
    fa2enDict[fa] = en;
});

function translate(str, dict, pattern) {
    return str.replace(pattern, function(c) { return dict[c]; });
}

function fa2en(str) {
    return translate(str, fa2enDict, /[۰-۹]/g);
}

function en2fa(str) {
    return translate(str, en2faDict, /[0-9]/g);
}

Here is a version which may be faster in some browsers. It uses a for-loop and range checking which relies on the fact that the digits are contiguous:

var en2faDict = {};
var fa2enDict = {};
"۰۱۲۳۴۵۶۷۸۹".split('').forEach(function(fa, en) {
    en = "" + en;
    en2faDict[en] = fa;
    fa2enDict[fa] = en;
});
en2faDict.low = '0'.charCodeAt(0);
en2faDict.high = '9'.charCodeAt(0);
fa2enDict.low = en2faDict['0'].charCodeAt(0);
fa2enDict.high = en2faDict['9'].charCodeAt(0);

function translate(str, dict) {
    var i, l = str.length, result = "";
    for (i = 0; i < l; i++) {
        if (str.charCodeAt(i) >= dict.low && str.charCodeAt(i) <= dict.high)
            result += dict[str[i]];
        else
            result += str[i];
    }
    return result;
}

function fa2en(str) {
    return translate(str, fa2enDict);
}

function en2fa(str) {
    return translate(str, en2faDict);
}
tom
  • 21,844
  • 6
  • 43
  • 36
  • 1
    MDN says this about the third parameter to replace : `A string specifying a combination of regular expression flags. The use of the flags parameter in the String.replace method is non-standard. Instead of using this parameter, use a RegExp object with the corresponding flags.` @ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace – HBP Jun 16 '13 at 12:55
  • Actually I was unaware of that third argument until I tried your code on Chrome and to my surprise it performed as you said. That sent me scurrying to find out why ;-) – HBP Jun 16 '13 at 13:11