6
\$\begingroup\$

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.)

\$\endgroup\$
1
\$\begingroup\$

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); });
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 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). \$\endgroup\$ – Scimonster Nov 22 '14 at 20:44
  • 1
    \$\begingroup\$ 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 \$\endgroup\$ – janos Nov 22 '14 at 21:04
3
\$\begingroup\$

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');
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.