jbuddy_13
jbuddy_13

Reputation: 1276

Javascript: forEach method within a function?

I'm new to JavaScript (I've used python for ~3yrs, and I'm currently trying to pick up the basics of JS syntax.) I am using codewars to facilitate learning.https://www.codewars.com/kata/5bb904724c47249b10000131/train/javascript

This problem gives the prompt

function points(games) {
  // your code here
}

The rules are:

if x>y -> 3 points
if x<y -> 0 point
if x=y -> 1 point 

My naive approach is to create a function which receives an array as an input and apply a forEach method to every element therein. However, I'm triggering the following error: Uncaught SyntaxError: Unexpected token 'else'

games = ["0:3","1:2","3:2"]

function points(games){
  let p = 0;
  games.forEach(
    function(game){
    let x = game.split(':')[0];
    let y = game.split(':')[1];
    if(x>y){
      p = p + 3};
    else if(x=y){
      p = p + 1};
    else {
      p = p + 0;
    };
  });
};

I'd like to better understand (A) why this error is beginning triggered and (B) what is the right way to accomplish this effect.

Edit: I likely need to cast x and y as a numeric type, however this isn't triggering the current error.

Upvotes: 0

Views: 182

Answers (5)

Vlad Jerca
Vlad Jerca

Reputation: 2224

The error you're getting is caused by the semicolon (;) that you put at the end if scopes:

if { ... }; else // unexpected token

LE: also, you have a bug in the second check, you're assigning the value of y to x. In Javascript comparisons are done with == or ===.

To expand a bit on equality comparisons:

  • == will attempt to cast the values before performing the comparison (eg: 1 == '1' => true)

  • === is a strict comparison operator that will only return true if both operands are of the same type and value (eg: 1 === 1 => true, 1 === '1' => false)

Here's a working example of your code:

function points(games) {
  let p = 0;

  games.forEach((game) => {
    let x = game.split(':')[0];
    let y = game.split(':')[1];
    if(x > y) {
      p = p + 3;
    } else if(x === y) {
      p = p + 1;
    } else {
      p = p + 0;
    }
  });
};

Below you'll find some other examples.

  1. Using array spreads and dropping the branch which adds 0 to the sum
function points(coordinates) {
  let p = 0;

  coordinates.forEach((coordinate) => {
    const [x, y] = coordinate.split(':');
    if(x > y) {
      p = p + 3;
    } else if(x === y) {
      p = p + 1;
    }
  });

  return p;
};
  1. My favorite approach when it comes to readability:
const points = (coordinates) =>  coordinates
  .map(coordinate => coordinate.split(':'))
  .reduce((points, [x, y]) => {
    if (x > y) { return points + 3; }
    if (x === y) { return points + 1; }
    return points;
  }, 0); 

Upvotes: 0

Some things to know about JS when you're coming from Python: the modern Array type has a lot of utility functions, so the code you've written can be done with just a few calls, and a ternary (which I really wish Python supported, like every other modern language)

function score(games = []) {
  // we could manually sum values, or we can use reduce() to do that for us.
  return games.reduce( (tally, game) => tally + calculateScore(game), 0);
}

/**
 * fun fact about JS: functions are "hoisted" i.e. they all get "moved"
 * during initial read-in to the top of the file, with their ordering
 * made entirely irrelevant: any function can have a function body that
 * calls any other function, because they're all at the same declaration level.
 */
function calculateScore(game = "0:0") {
  let [x, y] = game.split(":").map(parseFloat);
  return x > y ? 3 : x < y ? 0 : 1;
}

This uses some elementary modern JS:

  • default parameters, basically the same as you'd use in Python,
  • arrow functions, which are kind of like lambdas but also nothing like them. They're pretty important to know about though, if you want to write modern JS.
  • array.reduce, which can make summing values much easier, or much harder, depending on how complicated your code gets,
  • parseFloat which turns strings into floats (fun fact: every number in JS is a float, which is why you integers only go up to 2^53: past that (n+1) - n === 1 no longer holds)
  • the ternary operator, which Python is sorely lacking.

Also quite importantly: note that where you see parseFloat, what really happens is that array.map calls parseFloat with two arguments: the element, and its index in the array. For parseFloat, this is fine, because it only takes one argument. However, if you were to naively use parseInt, things would go horribly wrong: it takes two arguments, namely a string, and a radix.

Upvotes: 2

epascarello
epascarello

Reputation: 207531

Your code has a bunch of syntax errors. You are using too many semicolons. You do not add them on every line. You are working with strings and not numbers. You are not going a comparison in your else if, and you do not return anything form the method.

function points(games) {
  let p = 0;
  games.forEach(
    function(game) {
      // no reason to do work twice, split once
      const parts = game.split(':')
      const x = +parts[0]; // convert to a number
      const y = +parts[1]; // convert to a number
      if (x > y) {
        p = p + 3
      } else if (x === y) { // comparison is == or === and no semicolon
        p = p + 1;
      } else { // <-- we do not add semicolons to the blocks on if/else
        p = p + 0;
      } // <-- we do not add semicolons to the blocks on if/else
    });
  // your function did not return the calculation total
  return p
};

const games = ["0:3", "1:2", "3:2"];
const result = points(games);
console.log(result);

Upvotes: -2

lhoro
lhoro

Reputation: 438

forEach is not the best approach for this question. This question can be done with reduce function, which can be used to calculate the accumulated sum for an array.

const games = ["10:3","1:2","3:2", "3:3"]

//forEach approach
function points(games){
  let p = 0;
  games.forEach(game => {
    const [x, y] = game.split(':').map(parseFloat);
    x>y? p+=3 : x===y ? p+=1 : p+=0
  })
  return p
}


//reduce approach
//second parameter in reduce function is the initial value, which is 0 here
function pointsReduce(games){
  const sum = games.reduce((accumulate, game) => {
    const [x, y] = game.split(':').map(parseFloat);
    return accumulate+= x>y? 3 : x===y ? 1 : 0
  },0)
  return sum
}


console.log(points(games))
console.log(pointsReduce(games))

Upvotes: 1

Danil Sabirov
Danil Sabirov

Reputation: 353

Seems you messed the curly brackets positions. They has to be right before else:

 if(x>y){
      p = p + 3;
    } else if(x=y){
      p = p + 1;
    } else {
      p = p + 0;
    };

Upvotes: 0

Related Questions