Tara
Tara

Reputation: 77

Javascript expression test

I have this example to solve, its quite easy but there is a lots of obstracles:

/**
 * Write a JavaScript function to check
 * whether it is possible to replace $
 * in a given expression x $ y = z
 * with one of the four signs +, -, * or /
 * to obtain a correct expression.
 */

/**
 * If we replace $ in input with one of + - * /
 * do we get a valid mathematical expression?
 * @param {string} input String that is tested
 * @returns {boolean} Whether it could be valid expression
 */
const isReplacable = input => {
    /**
     * Please fill in your code here
     */

    return false;
};

const TEST_STRINGS = [
    '3 $ 5 = 8',
    '2 $ 6 = 3',
    '5$5=25',
    '2 $ 1 = 2',
    '3 $ 4 = 5',
    '6 $ 4 = 3',
    '3 $ 2 = 1',
    '4 $6= 24',
    '3$ 2= 6',
    '30$ 15 =2',
];

export const runChecks = () => {
    TEST_STRINGS.forEach(expression => {
        console.log(`${expression}: ${isReplacable(expression) ? 'VALID' : 'INVALID'}`);
    });
};

I tried to finish it like this:

1, make parser

function countIt(test) {
    const numbers = test.split('');
    const target = ' ';

    var i = 0;
    while (i < numbers.length) {
      if (numbers[i] === target) {
          numbers.splice(i, 1);
      } else {
          ++i;
      }
    }
    newNumbers = numbers;
    console.log(numbers);

    const newTarget = '$';
    var i = 0;
    while (i < newNumbers.length) {
        if (newNumbers[i] === newTarget) {
            newNumbers.splice(i, 1);
        } else {
            ++i;
        }
        }
    console.log(newNumbers);
    
    finalNumbers = newNumbers;
    const finalTarget = '=';
    var i = 0;

    while (i < finalNumbers.length) {
        if (finalNumbers[i] === finalTarget) {
            finalNumbers.splice(i, 1);
        } else {
            ++i;
        }
    }

    console.log(finalNumbers);

    if (finalNumbers.length)

    firstNumber = finalNumbers[0];
    secondNumber = finalNumbers[2];
    resultNumber = finalNumbers[4];

    const result = parseInt(firstNumber) + parseInt(finalNumbers[1])
    
    if (parseInt(firstNumber) + parseInt(secondNumber ) == resultNumber) {
        console.log('+');
    }
    else if (parseInt(firstNumber) - parseInt(secondNumber ) == resultNumber) {
        console.log('-');
    }
    else if (parseInt(firstNumber) * parseInt(secondNumber ) == resultNumber) {
        console.log('*');
    }
    else if (parseInt(firstNumber) / parseInt(secondNumber ) == resultNumber) {
        console.log('/');
    }
    else {
        console.log('false');
    }
}

But there is so many problems to solve like 2-digits, or missing white-space after expression so i think my solution it too overenginered and isnt working correctly anyway

Do u have any better solutions ? Thanks for your tips ;)

Upvotes: 1

Views: 133

Answers (5)

X-Ray
X-Ray

Reputation: 57

// expression: "number $ number = number"
// left: [number, number]
// right: number
const lexer = (expression) => {
    const [left, right] = expression.split('=')
    return {
        left: left.split('$').map(i => Number(i)),
        right: Number(right)
    }
}

const calculate = (n1, n2) => {
    let r = []
    r.push(n1 + n2)
    r.push(n1 - n2)
    r.push(n1 * n2)
    if (n2 !== 0) {
        r.push(n1 / n2)
    }
    return r
}

const isReplacable = (input) => {
    const {left, right} = lexer(input)
    const cal = calculate.apply(null, left)
    
    return cal.some(i => i === right)
}

The short explanation.

  1. A minimized lexer for expression. the binary operation can be explained as left-hand and right-hand by the '=' operator. and split tokens by '$'. You can google parser for more complex compiler knowledge. In this case, the String API works better and easier.
  2. try to calculate the binary operation by two numbers.

I put the code on https://runkit.com/kennywho/javascript-expression-test

If the expression gets more complex, you can refer to the 24-game and the math expression parser. you can google or find it on Leetcode.

Upvotes: 1

Sakil
Sakil

Reputation: 652

let evalBySign = (first, second, answer, sign) => eval(`(${first}) ${sign} (${second})==${answer}`);
let operation = {
    '+': function(first, second, answer) {
        //Way 1
        return Number(first) + Number(second) == answer;
    },
    '-': function(first, second, answer) {
        //Way 2
        return eval(`(${first}) - (${second})==${answer}`);
    },
    '*': function(first, second, answer) {
        //Way 2 as function
        return evalBySign(first, second, answer, '*');
    }
};
['/', '%'].forEach(sign => {
    operation[sign] = function(first, second, answer) {
        return evalBySign(first, second, answer, sign);
    }
});

function isReplacable(input) {
    input = input.replace(/\s+/g, '');
    let validEq = /\d+\$\d+=\d+/.test(input);
    if(!validEq)
        return false;
    
    let numbers = input.split(/[\$=]/);
    if(numbers.length != 3)
        return false;

    let [first, second, answer] = numbers;
    let operationEntries = Object.entries(operation);
    let isReplacelable = false;
    for (let entry of operationEntries) {
        if(entry[1](first, second, answer)) {
            console.log(`${input} equation matched using '${entry[0]}' sign`);
            isReplacelable = true;
            break;
        }
    }
    return isReplacelable;
}

const TEST_STRINGS = [
    '3 $ 5 = 8',
    '2 $ 6 = 3',
    '5$5=25',
    '2 $ 1 = 2',
    '3 $ 4 = 5',
    '6 $ 4 = 3',
    '3 $ 2 = 1',
    '4 $6= 24',
    '3$ 2= 6',
    '30$ 15 =2',
    '3 $ = 1 4',
    '2 3 5'
];

const runChecks = () => {
    TEST_STRINGS.forEach(expression => {
        console.log(`${expression}: ${isReplacable(expression) ? 'VALID' : 'INVALID'}`);
    });
};
runChecks();

Upvotes: 1

derpirscher
derpirscher

Reputation: 17390

You can do with regex:

Fetch out the three numbers and ignore any whitespaces. If the teststring is in an invalid format (for instance contains invalid characters, no operators, to many or to few numbers, ...) it will return false immediately, because the regex can't match.

Otherwise just check if any of the four operations on the first two numbers results in the third number ...

const isReplaceable = (input) => {
  let match = /^\s*(\d+)\s*\$\s*(\d+)\s*=\s*(\d+)\s*$/.exec(input);
  if (!match) return false;
  return +match[1] + +match[2] == +match[3] ||
         +match[1] - +match[2] == +match[3] ||
         +match[1] * +match[2] == +match[3] ||
         +match[1] / +match[2] == +match[3] 
}

const TEST_STRINGS = [
    '3 $ 5 = 8',
    '2 $ 6 = 3',
    '5$5=25',
    '2 $ 1 = 2',
    '3 $ 4 = 5',
    '6 $ 4 = 3',
    '3 $ 2 = 1',
    '4 $6= 24',
    '3$ 2= 6',
    '30$ 15 =2',
];

for (let t of  TEST_STRINGS)
  console.log(t, isReplaceable(t));

Upvotes: 1

Etheryte
Etheryte

Reputation: 25319

One way to sidestep the whitespace problem would be to (ab)use the fact that parseInt() is lenient and ignores whitespace. This is of course a hack and not how you should generally write programs, but in this case it makes for a fairly elegant solution.

const isReplaceable = (input) => {
  const [a, b, c] = input.split(/\$|=/).map(item => parseInt(item, 10));
  return (
    a * b === c ||
    a / b === c ||
    a + b === c ||
    a - b === c
  );
}

const isReplaceable = (input) => {
  const [a, b, c] = input.split(/\$|=/).map(item => parseInt(item, 10));
  return (
    a * b === c ||
    a / b === c ||
    a + b === c ||
    a - b === c
  );
}

const TEST_STRINGS = [
    '3 $ 5 = 8',
    '2 $ 6 = 3',
    '5$5=25',
    '2 $ 1 = 2',
    '3 $ 4 = 5',
    '6 $ 4 = 3',
    '3 $ 2 = 1',
    '4 $6= 24',
    '3$ 2= 6',
    '30$ 15 =2',
];

TEST_STRINGS.forEach(item => console.log(isReplaceable(item)));

Upvotes: 3

Erim Varış
Erim Varış

Reputation: 94

This should probably works

const isReplacable = input => {
/**
 * Please fill in your code here
 */
    let result = input.split('=')[1].replace(' ','');
    let inputs = input.split('$');
    let input1 = inputs[0].replace(' ','');
    let input2 = inputs[1].replace(' ','');

    if(Number(input1) + Number(input2) == Number(result)) {
       // its sum, do things
    } else if(Number(input1) - Number(input2) == Number(result)) {
       // decrement
    } else if(Number(input1) / Number(input2) == Number(result)) {
       // division
    } else if(Number(input1) * Number(input2) == Number(result)) {
       // multiply
    } else {
       // wrong input
    }

    return false;
};

I hope you got it.

Upvotes: 0

Related Questions