# Substituting Hebrew letter variants

I have two functions that replace letters. In one function, I'm replacing a regular Hebrew letter with its final form, and the other function undoes this operation. (Consider it like lowercase/uppercase, except only a few letters have both forms.)

I created an object where the keys are the regular forms and the values are the final forms. In each function, I iterate over the object and run a regex - in one function it's map[i] -> i; the other is i -> map[i].

Would it be worth it / even possible to abstract this for loop into a function?

var map = {
כ: 'ך',
מ: 'ם',
נ: 'ן',
פ: 'ף',
צ: 'ץ'
};

exports.toFinal = function(text) {
for (var i in map) {
text = text.replace(new RegExp(i,'g'), map[i]);
}
return text;
}

exports.toRegular = function(text) {
for (var i in map) {
text = text.replace(new RegExp(map[i],'g'), i);
}
return text;
}


(exports is Node.js's exports object, and the code is implicitly running in a closure.)

You could extract the logic of the for loop to a common function, to be reused by both exports.toFinal and exports.toRegular, by passing in a function as parameter:

function applyReplacements(text, patternReplacementPairGetter) {
for (var i in map) {
var pair = patternReplacementPairGetter(i, map[i]);
var pattern = pair.pattern;
var replacement = pair.replacement;
text = text.replace(new RegExp(pattern, 'g'), replacement);
}
return text;
}

function patternReplacementPair(pattern, replacement) {
return {
pattern: pattern,
replacement: replacement
};
}

exports.toFinal = function(text) {
return applyReplacements(text, function(k, v) { return patternReplacementPair(k, v); });
}

exports.toRegular = function(text) {
return applyReplacements(text, function(k, v) { return patternReplacementPair(v, k); });
}

• Wow, that code is a lot longer than the original! Also, i wouldn't attach those other functions to the exports object, but just leave them in the implicit closure (this is NodeJS). – Scimonster Nov 22 '14 at 20:44
• I don't see length as a concern when decomposing methods and reducing duplication of logic. As per your second point about closures, I updated my answer, and improved somewhat the naming – janos Nov 22 '14 at 21:04

I would opt for another implementation.

You iterate over your map and for each value, you replace a character in the source string. So for n characters in your map, you run n replacement operations for a length of m. Given, that n = m, that takes n^2 steps.

I would make two maps; one for each direction: (for simplicity reasons, I chose latin letters)

var form1 = {
'a':'A',
'b':'B'
};

var form2 = {
'A':'a',
'B':'b'
};


If I need to know, whether there is a translation, I could do a simple operation form1['a'] or form1['x'].

A translation - independent of the direction - could be defined as:

function translate(x){
return (map[x])?map[x]:x;
}


Is a letter in the map, translate it, else return the original letter.

To specify the direction, you could define a closure, passing in the appropriate map:

 function makeTranslation(map){
return function translate(x){
return (map[x])?map[x]:x;
}
}


If you want to translate more than one character, you have to split the string and translate each character. After that join the result:

function translate(expression, direction){
return expression.split("").map(direction).join("");
}


Which results in two functions:

function toFinal(expression){
return translate(expression, makeTranslation(form1));
}

function toRegular(expression){
return translate(expression, makeTranslation(form2));
}


The resulting code looks like this:

var form1 = {
'a':'A',
'b':'B'
};

var form2 = {
'A':'a',
'B':'b'
};

function makeTranslation(map){
return function translate(x){
return (map[x])?map[x]:x;
}
}

function translate(expression, direction){
return expression.split("").map(direction).join("");
}

function toFinal(expression){
return translate(expression, makeTranslation(form1));
}

function toRegular(expression){
return translate(expression, makeTranslation(form2));
}

console.log(toRegular(toFinal("aabbcbb"))==='aabbcbb');
console.log(toFinal(toRegular("AABBcBB"))==='AABBcBB');

• An invert() function, such as the one in Underscore.js, would be useful. – 200_success Nov 25 '14 at 4:37