Alan M.
Alan M.

Reputation: 1369

regex to help tally parenthetical values from string

Currently, I use this code to total parenthetical values in a string...

if ((string.match(/\((\d+)\)/g)||[]).length > 0) {
    var total = 0;
    string.replace(/\((\d+)\)/g, function(outerValue, innerValue){
        if (!isNaN(innerValue.toString().trim())) {
            total = total + Number(innerValue.toString().trim());
        }
    });

    value = total;
}

...so the string...

(2) dark chocolate, (2) milk chocolate, and (1) white chocolate

...totals 5.

Not willing to leave well-enough alone, I thought it would be cool if I could be a bit fancier and interpret different types of operations, so that someone could write.

(2) dark + (2) milk - (1) white
    -or-
(2) dark and (2) milk minus (1) white

So I changed my code to...

if ((string.match(/\((\d+)\)/g)||[]).length > 0) {
    var total = 0;
    string.replace(/^\((\d+)\)|and\s\((\d+)\)|plus\s\((\d+)\)|\+\s\((\d+)\)/g, function(outerValue, innerValue){
        if (!isNaN(innerValue.toString().trim())) {
            total = total + Number(innerValue.toString().trim());
        }
    });

    value = total;
}

...but the innerValue returns as undefined. I am able to extract the values when I test with the validator in regex101.com, but not in Javascript.

What am I doing incorrectly?

p.s. Obviously, my code is not complete (in addition to being wrong). Ultimately, I would list all of the operator possibilities (e.g., "+", "plus", "and", "less", "minus", "-", etc.) and would examine the string in outerValue to determine the operator. And, of course, I need to write the logic for commas within a sentence (e.g., allow a single operator in the sentence and apply the operation to each item).

Upvotes: 1

Views: 85

Answers (3)

Alan M.
Alan M.

Reputation: 1369

chiliNUT's explanation was extremely helpful, as was his subsequent suggestion in his reply. Inspired to explore some variations, I ended up coding it as follows:

if ((string.match(/\((\d+)\)/g)||[]).length > 0) {
    var total = 0;

    string.match(/^\((\d+)\)|,\s+\((\d+)\)|and\s+\((\d+)\)|plus\s+\((\d+)\)|\+\s+\((\d+)\)|\-\s+\((\d+)\)|minus\s+\((\d+)\)|less\s+\((\d+)\)|subtract\s+\((\d+)\)|times\s+\((\d+)\)|multiply\s+\((\d+)\)|x\s+\((\d+)\)|\*\s+\((\d+)\)/g).forEach(function(element, index){
        element.replace(/\((\d+)\)/g, function(m, p){
            if (!isNaN(p.toString().trim())) {
                p = Number(p);

                if (element.match(/^(minus|less|substract|\-)/g)) {
                    total = total - p;
                }
                else if (element.match(/^(times|multiply|x|\*)/g)) {
                    total = total * p;
                }
                else {
                    total = total + p;
                }
            }
        });
    });
}

I'm sure there's a more efficient way, but for my deepest RegEx dive thus far, and given that it actually works, I figure it's a good start.

Upvotes: 0

chiliNUT
chiliNUT

Reputation: 19573

Your argument names (outerValue, innerValue) aren't really accurate. The arguments to the replace function are

function replace(match, p1, p2, ..., pn, offset, string)

So you have

     p1               p2              p3            p4
      |               |               |             |   
/^\((\d+)\)|and\s\((\d+)\)|plus\s\((\d+)\)|\+\s\((\d+)\)/g
                              |
                            match

So when you run (2) dark and (2) milk minus (1) white through your replacer function:

The first match "(2)" has p1=2 since it corresponds to the 1st parenthetical set in your regex, ie, in the first or group. You then have p2=undefined, p3=undefined, p4=undefined.
The next match "and (2)" has p1=undefined since this matches up with the 2nd or group in your regex, so p1=undefined, p2=2, p3=undefined, p4=undefined

See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter

Upvotes: 2

mrzasa
mrzasa

Reputation: 23317

I would suggest to build a add a parser that would interpret the string. I'd user regexps to extract tokens from text ((1) => 1, - => - ) and pass it to the parser. It would keep track of values and operators and perform calculations.

This thread may be related: https://stackoverflow.com/a/380487/580346

Upvotes: 0

Related Questions