If you encounter an unrecognized operator in Javascript, make sure to handle this error and return

I have a script that identifies operators in an array and uses them to calculate based on another array. Below is the script:

function interpret(...args) {
  let operators = args[1]; //access the operators array
  let values = args[2] //numbers except the first one
  return values.reduce((ac, val, i) => {
    //check the operator at index 'i' while iterating through 'values'
    if (operators[i] === '+') return ac + val;
    if (operators[i] === '-') return ac - val;
    if (operators[i] === '*') return ac * val;
    if (operators[i] === '/') return ac / val;
    else return -1;
  }, args[0]) //'ac' initially set to first value
}

console.log(interpret(1, ["+"], [1]))
console.log(interpret(4, ["-"], [2]))
console.log(interpret(1, ["+", "*"], [1, 3]))
console.log(interpret(5, ["+", "*", "-"], [4, 1, 3]))
console.log(interpret(10, ['*', '$', '+'], [5, 3, 2])) //Fails in this case and returns 1

Please assist in resolving this issue. Thank you

Answer №1

One way to handle invalid operators in the reduce function is by setting a flag like invalid. This ensures that only -1 is returned if an invalid operator is detected:

function interpret(...args) {
  let operators = args[1]; //retrieve the operators array
  let invalid = false; // no invalid operators initially
  let values = args[2] //numbers except for the first one.
  return values.reduce((ac, val, i) => {
    //check the operator at 'i' while iterating through 'values'
    if (!invalid) { // execute this block only if 'invalid' is false:
      if (operators[i] === '+') return ac + val;
      if (operators[i] === '-') return ac - val;
      if (operators[i] === '*') return ac * val;
      if (operators[i] === '/') return ac / val;
    }
    // If 'invalid' is true or none of the above conditions match, set 'invalid' to true
    invalid = true; 
    return -1; 

  }, args[0]) //'ac' starts with the initial value.
}

console.log(interpret(1, ["+"], [1]))
console.log(interpret(4, ["-"], [2]))
console.log(interpret(1, ["+", "*"], [1, 3]))
console.log(interpret(5, ["+", "*", "-"], [4, 1, 3]))
console.log(interpret(10, ['*', '$', '+'], [5, 3, 2])) // -1

Alternatively, another approach using recursion can be implemented:

const oper = {
  '+': (a, b) => a + b,
  '-': (a, b) => a - b,
  '*': (a, b) => a * b,
  '/': (a, b) => a / b
};

const interpret = (n, [fc, ...calcs], [fn, ...nums]) => {
  if(fc === undefined) return n; // Base case for ending recursion
  if(!(fc in oper)) return -1; // Handle invalid operator case
  return interpret(oper[fc](n, fn), calcs, nums)
}

console.log(interpret(1, ["+"], [1]))
console.log(interpret(4, ["-"], [2]))
console.log(interpret(1, ["+", "*"], [1, 3]))
console.log(interpret(5, ["+", "*", "-"], [4, 1, 3]))
console.log(interpret(10, ['*', '$', '+'], [5, 3, 2])) // -1

Answer №2

My suggestion would be to store all the possible operations in a map of operator => function, like:

const operations = {
  "+": function(a, b) { return a + b; },
  "-": function(a, b) { return a - b; },
  "*": function(a, b) { return a * b; },
  "/": function(a, b) { return a / b; }
};

You can then retrieve the operation from this map and if it is not found, return NaN to convert the final result to NaN. Finally, check if the result is NaN and return -1 if it is. Additionally, I recommend declaring variables instead of using arguments.

const operations = {
  "+": (a, b) => a + b,
  "-": (a, b) => a - b,
  "*": (a, b) => a * b,
  "/": (a, b) => a / b
};

function empty() { return NaN; }

function numberOrDefault(res, def) { return isNaN(res) ? def : res; }

function interpret(start, operators, values) {
  return numberOrDefault(values.reduce((acc, val, i) => (operations[operators[i]] || empty)(acc, val), start), -1);
}

console.log(interpret(1, ["+"], [1]));
console.log(interpret(4, ["-"], [2]));
console.log(interpret(1, ["+", "*"], [1, 3]));
console.log(interpret(5, ["+", "*", "-"], [4, 1, 3]));
console.log(interpret(10, ['*', '$', '+'], [5, 3, 2]));

If you prefer not to make any changes, here is a solution with minimal alterations:

function interpret(...args) {
  let operators = args[1]; //get the operators array
  let values = args[2] //numbers expect the first one.
  let result = values.reduce((ac, val, i) => {
    //check the operator at the 'i' on which we are on while iterating through 'value'
    if (operators[i] === '+') return ac + val;
    if (operators[i] === '-') return ac - val;
    if (operators[i] === '*') return ac * val;
    if (operators[i] === '/') return ac / val;
    else return NaN;
  }, args[0]); //'ac' is initially set to first value.
  return isNaN(result) ? -1 : result;
}

console.log(interpret(1, ["+"], [1]))
console.log(interpret(4, ["-"], [2]))
console.log(interpret(1, ["+", "*"], [1, 3]))
console.log(interpret(5, ["+", "*", "-"], [4, 1, 3]))
console.log(interpret(10, ['*', '$', '+'], [5, 3, 2]))

Answer №3

In my approach, I would implement a "parsing" phase before moving on to interpretation. This phase involves scanning both the operations and values to ensure the input is valid and sensible. To handle any unexpected issues during interpretation, I would wrap the process in a try/catch block.

Additionally, I find inspiration in Nick's solution and will incorporate some elements from it into my code.

const oper = {
  '+': (a, b) => a + b,
  '-': (a, b) => a - b,
  '*': (a, b) => a * b,
  '/': (a, b) => a / b
};
const opers = Object.keys(oper)

function interpret(...args) {
  let operators = args[1];
  let values = args[2]

  const validOperators = operators.every(op => opers.includes(op) )
  const validValues = values.every(val => typeof val === "number" )

  if(!validOperators || !validValues)
    return -1

  try {
    return values.reduce((ac, val, i) => {
        const curOper = operators[i]
        return oper[curOper](ac, val)
      }, args[0])
  } catch(e){
    return -1
  }
}

console.log(interpret(1, ["+"], [1]))
console.log(interpret(4, ["-"], [2]))
console.log(interpret(1, ["+", "*"], [1, 3]))
console.log(interpret(5, ["+", "*", "-"], [4, 1, 3]))
console.log(interpret(10, ['*', '$', '+'], [5, 3, 2]))
console.log(interpret(10, ['*', '*', '+'], [5, 3, "dog"]))

Answer №4

The demonstration below showcases a function called calc:

  • Signature;

    calc(operators, numbers)

  • The first parameter is an array of integers ranging from 0 to 3 representing operators:

    0: +, 1: -, 2: *, 3: /
    example: [0, 1, 3, 3, 2] represents +, -, /, /, *

  • The second parameter is an array of floats on which calculations will be performed.
  • For optimal functionality:

    • The length of the second parameter should be exactly one more than the length of the first parameter.

      example:

      calc([1, 0, 3], [24, 88, 185, 11]) //11

  • Handling non-optimal input scenarios:

    • If the length of the second parameter is two or more greater than the length of the first parameter, the extra values in the second parameter will be truncated so that it's length remains one more than the first parameter.

      example:

      calc([1, 0, 3], [24, 88, 185, 11, 325]) //11

    • If both parameters have equal lengths or if the first parameter is longer than the second parameter, the additional numbers in the first parameter will be ignored.

      example:

      calc([1, 0, 3, 2, 1], [24, 88, 185, 11]) //11

    • If there's only one number in the second parameter or if the first parameter includes a value other than [0-3], that particular number is returned. This simulates how calculators handle incorrect inputs.

      example: calc([1, 0, 3], [11]) //11

I chose to represent the operators as an array of numbers instead of strings for simplicity and accuracy. There should be no chance of receiving a non-numeric input unintentionally. The ternary operator chain determines the operator used during each iteration of the reduce() method. By not specifying an initial accumulator value, the operators come into play starting from the second iteration, ensuring an alternating pattern is followed:

// First iteration: 
numbers[0] // Accumulator
 // Second iteration:
operators[0], numbers[0] /* Accumulator */ numbers[1] // currentValue

Demo

/*
@Params
|-operators: Array of numbers | range [0-3] 
| 0: +, 1: -, 2: *, 3: /
|-numbers: Array of floats
| numbers.length = operators.length + 1
@Return
|-a float
*/
const calc = ([...opr], [...num]) => {
  return num.reduce((acc, cur, idx) => {
    if (num.length - opr.length !== 1) {
      num.length = opr.length + 1
    }
    if (idx > 0) {
      let op = opr[idx - 1];
      let exp = (op === 0) ? acc + cur : (op === 1) ? acc - cur : (op === 2) ? acc * cur : (op === 3) ? acc / cur : acc + 0;
      return exp;
    }
  });
}

console.log(calc([1], [1, 5, 22]));
console.log(calc([0, 1], [55, 2, 0.3]));
console.log(calc([2, 3], [22, 6]));
console.log(calc([3, 1, 2], [51, 3, 3, 5]));
console.log(calc([1, 0, 3], [24, 88, 185, 11]));
console.log(calc([0], [94]));
console.log(calc([5, 6, 1], [94, 22]));

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Having difficulties fetching images from the Twitter API

I'm encountering an issue with a simple Twitter API request for a specific tweet. Although the tweet should have multiple images attached to it, the media entity object is returning empty. Here's the tweet in question: https://twitter.com/talonc ...

Passing parameters from a parent component to a child component and then back to the parent component

I have a unique custom component called InputWithButton that has a distinct structure: const InputWithButton = ({ type = "text", id, label, isOptional, name, placeholder = "", value = "", showPasswordReset, error, isDisabled, buttonLabel, handleChange ...

NodeJS process that combines both client and server functionality

Attempting to develop a test echo server with both client and server in the same node process. When the code is split into two files (server and client), it functions correctly, but when combined into one file, it fails. How can I make it work within a sin ...

Delving into XFN data parsing using Jquery, part two

I need some assistance with correctly parsing the REL attribute on A tags. I have already figured out how to match most XFN values, but there are two that I'm struggling with: "co-worker" and "co-resident". The hyphen seems to be causing an issue with ...

Separate the express node js into a pair

I'm attempting to divide a code into two parts using express. Here is how I approached it: app.js var express = require('express'); var app = express(); var stud = require('./grades'); var port = process.env.PORT || 3000; stud. ...

Using Regular Expressions in JavaScript for Filtering with Angular.js

I am looking to create a custom filter in Angular.js. When an object has a name == null, and I add "u" to the filter->, it results in the return of the object where name == null because re.test(null)=true. However, other characters are returning false. ...

Can I change the names of the data retrieved from this API?

I'm looking to customize the names fetched from an external API in my <span>{{stats.team.name}}</span>. Currently, the output displays full club names like: Manchester City FC Manchester United FC Wolverhampton Wanderers FC Instead, ...

Tips on avoiding a div from causing its parent's mousemove event to activate if it is concealed

I have a collection of media content with a set of controls positioned above it. I am attempting to implement functionality where the controls will disappear when the user's mouse is inactive and reappear upon mouse movement. However, I am facing an ...

Storing information in an array and converting it to JSON using dual nested foreach loops in PHP

I need to retrieve data from two tables that have no relation. I am using a foreach loop to fetch all the data by a selected key and combine the data from both tables into an array. How can I store the data in an array while fetching data from two tables u ...

I'm encountering an undefined JavaScript variable, how should I proceed?

$(function(){ //Location was "set". Perform actions. $("#geocodesubmit").click(function(){ var geocoder = new google.maps.Geocoder(); geocoder.geocode( { 'address': address}, function(results, status) { if (status ...

Resolve the issue of autofill data overlapping with field labels

My form incorporates the Canada Post AddressComplete tool, which functions similarly to Google Maps Autocomplete. As you type your address, suggestions appear in a drop-down menu. However, after selecting an address, the text in the Postal Code and City f ...

Guide to storing data in the browser's local storage with a Node.js Express backend

Up until this point, I relied on cookies to store preferences for my websites. However, as time has passed, the data stored in these cookies has grown too large and now exceeds the limit of 4096 characters. Therefore, I need to explore an alternative meth ...

What is the best way to identify a modification in a select element within React?

My HTML form has a dropdown menu for users to select colors, and I'm attempting to determine when a new color is chosen. Despite adding the `onchange` attribute with a `console.log()` function, nothing appears in the console when I pick a different co ...

Having trouble setting a value in a Vue.js variable

Upon assigning a value retrieved from the firebase collection, I encountered the following error message. Error getting document: TypeError: Cannot set property 'email' of undefined at eval (Profile.vue?5a88:68) Here is the code snippet in que ...

Attempting to remove the current click event listener and add a new event listener

I am attempting to remove the click event binding and replace it with another click event after the button has been clicked once. <small id="selectall_agriculture" onclick="refineSearch.testing('agriculture')">(select all)</small> O ...

Split a string containing integer and decimal values into an array

I encountered an issue when converting a string to an array: var data = "[[1, 2, 3,], [3, 2, 1]]"; var array = JSON.parse(data.replace(/,\s*]/g, ']')); This problem arises when handling floating point numbers in the input: var data = "[[2 ...

I want to know the process of increasing the id name of a div and the name of the content stored

I'm struggling to understand how to implement a for loop and increment multiple items. Can anyone offer guidance? Here's my jQuery code: var counter1 = localStorage.getItem('counter1item'); if (counter1 == null){ $("#i1").html(&ap ...

The module 'SharedModule' has imported an unexpected value of 'undefined'

When working with an Angular application, I want to be able to use the same component multiple times. The component that needs to be reused is called DynamicFormBuilderComponent, which is part of the DynamicFormModule. Since the application follows a lib ...

What prevents this code from causing a buffer overflow?

I'm encountering an unexpected behavior with the code I've written: #include <stdio.h> int main() { char s[10]; while (fscanf(stdin, "%10s", s) != 1) { } printf("%s", s); } Surprisingly, the code ru ...

Forward slashes in JSON output from Node.js

I recently encountered an issue with receiving JSON data from a Node server that contained unnecessary slashes, making it difficult to parse. The JSON data looked like this: "{\"responseCode\":200,\"headers\":{\"Access-Control-All ...