Javascript string replaced by calculations

Is there a way to resolve math expressions in strings in javascript? For example, suppose I want to create the line "Tom has 2 apples, Lucy has 3 apples. Together they have 5 apples", but I want to be able to replace them in variables. I can do it with string replacement:

string = "Tom has X apples, Lucy has Y apples. Together they have Z apples";
string2 = string.replace(/X/, '2').replace(/Y/, '3').replace(/Z/, '5');

      

However, it would be better if I could use X + Y instead of variable Z. Now I could also perform string substitution for X + Y and replace it with the correct value, but this can get messy when you try to solve all possible inline calculations which I might want to do. I guess I'm looking for a way to achieve this:

string = "Something [X], something [Y]. Something [(X+Y^2)/(5*X)]";

      

And for the [___] parts, the expressions to be resolved should be understood before being replaced back into a string.

Thanks for your help.

+2


a source to share


4 answers


There is no direct, built-in way (well, okay, maybe there is - see below), but if you are using a function callback replace

where the replacement can be a function and not a string (the return value is a replacement), you can implement this rather easy.

For example, let's say you are using Ruby notation #{xyz}

for your placeholders. This code goes through those:

var mappings, str;

str = "One #{X} three #{Y} five";
mappings = {
    "X": 2,
    "Y": 4
};
str = str.replace(/\#\{([^#]+)\}/g, function(match, key) {
    var result;

    result = mappings[key];
    /* ...processing here */
    return result;
});

      

The resulting string One 2 three 4 five

, because #{X}

u #{Y}

were replaced by search. You can look at the key and see if it is an expression and needs to be evaluated rather than just searched. This assessment is where your real work comes in.

Now you can use with

it eval

to support expressions; change the line result = mapping[key];

above:

    with (mappings) {
        result = eval(key);
    }

      



If you feed a string "One #{X} three #{Y} five #{X + Y * 2}"

into this, the result One 2 three 4 five 10

is because 2 + 4 * 2

= 10.

This works because it with

attaches the given object to the top of the scope chain, so the first thing that gets checked when resolving an unqualified reference (for example X

) and eval

executes Javascript code - and therefore can evaluate expressions - and magically does so within the scope in which it is called. But be careful; as Eric pointed out, not all operators are the same in different forms of expression, and in particular Javascript interprets ^

as "bitwise XOR" and not "in power". (It doesn't have an exponent operator, you must use Math.pow

.)

But you need to be very careful about things like this with

, and eval

(each in its own way) can be problematic. But the main problems with with

are that it is difficult to tell where something is coming from or where it will go if you are doing a task that you are not; and the main problems with it eval

come from using it to interpret strings that you have no control over. As long as you keep the guarantees in place and are aware of the problems ...

Incorporating this feature into a feature:

function evaluate(str, mappings) {
    return str.replace(/\#\{([^#]+)\}/g, function(match, key) {
        var result;

        with (mappings) {
            result = eval(key);
        }

        return result;
    });
}
alert(evaluate(
    "The expression '(#{X} + #{Y}) * 2' equals '#{(X + Y) * 2}'",
    {"X": 2, "Y": 4}
)); // alerts "The expression '(2 + 4) * 2' equals '12'"
alert(evaluate(
    "The expression '(#{X} + #{Y}) * 2' equals '#{(X + Y) * 2}'",
    {"X": 6, "Y": 3}
)); // alerts "The expression '(6 + 3) * 2' equals '18'"

      

+4


a source


The only way I can achieve this is with a templating engine like jTemplates . Also see the answers to this question .



+1


a source


Good question:

function substitutestring(str,vals)
{
    var regex = /\[[^\]]*\]/gi;
    var matches = str.match(regex);
    var processed = [];

    for(var i = 0; i<matches.length; i++)
    {
        var match = matches[i];
        processed[match] = match.slice(1,-1);

        for(j in vals)
        {
            processed[match] = processed[match].replace(j,vals[j]);
        }

        processed[match] = eval("("+processed[match]+")");
    }

    for(var original in processed)
    {
        str = str.replace(original,processed[original]);
    }
    return str;
}

document.write(
    substitutestring(
        "[x] + [y] = [x+y]",
        {"x": 1, "y": 2}
    )
);

      

+1


a source


In ES6, you can now use template strings :

var X = 2, Y = 3; string = Tom has ${X} apples, Lucy has ${Y} apples. Together they have ${X+Y} apples

;

0


a source







All Articles