Search code examples
javascriptarraysprimes

Missing last element in array


I'm trying to sum all primes up to a number.

First I removed all evens and pushed the rest to an odd-only array.

Then I'm going over the array and splicing all the numbers that divide by other numbers but 1 and themselves, and replacing them with zero.

Everything seemed to go okay, however, notice in my first console.log that the last element is 977 (which is the num passed).

A line later I forEach the array and print all numbers over 970 but 977 isn't there.

Any ideas on how this happened? (I ruled out voodoo..)

function sumPrimes(num) {
  
  var arr = [2];
  for (var i = 3; i <= num; i++) {
    if (i % 2 !== 0) {
      arr.push(i);
    }
  }

  console.log(arr);
  arr.forEach(function(x) {
    
    if(x > 970){
      console.log(x);
    }
    
    if (x > 3) {
      for (var j = 3; j < x; j += 2) {
        if (x % j == 0) {
          arr.splice(arr.indexOf(x), 1, 0);
        }
      }
    }
  })

  // console.log(arr);
  var res = arr.reduce(function(acc, val) {
    return acc + val;
  }, 0)
  console.log(res);
}

sumPrimes(977);

Solution

  • The issue is how you're manipulating the array and the array never actually ends up containing 977.

    What happens is you find a non-prime and alter the array in place so that it's now 0 but you continue to run checks on that prime.

    This causes any following indexOf lookup for that value to return -1 and hence you get unexpected results.

    if (x % j == 0) {
       arr.splice(arr.indexOf(x), 1, 0); // This won't work after the first assign to 0
    }
    

    You can easily fix this by inserting a 'break;' immediately following the splice to short circuit the loop, however you can easily simplify & dramatically speed this up using the index parameter available in a forEach to remove the array position lookup entirely.

    function sumPrimes(num) {
    
      var arr = [2];
      for (var i = 3; i <= num; i++) {
        if (i % 2 !== 0) {
          arr.push(i);
        }
      }
    
      console.log(arr);
      arr.forEach(function(x, idx) {
    
        if(x > 970){
          console.log(x);
        }
    
        if (x > 3) {
          for (var j = 3; j < x; j += 2) {
            if (x % j == 0) {
              arr[idx] = 0;
              break;
            }
          }
        }
      })
    
      // console.log(arr);
      var res = arr.reduce(function(acc, val) {
        return acc + val;
      }, 0)
      console.log(res);
    }
    
    sumPrimes(977);


    EDIT: If anyone wants a simple, more efficient method of determining whether a number is prime or not, I suggest using the Trial Division method, not the one above.

    You can find a succinct, well tested example on Rosetta code:

    function isPrime(n) {
      if (n == 2 || n == 3 || n == 5 || n == 7) {
        return true;
      } else if ((n < 2) || (n % 2 == 0)) {
        return false;
      } else {
        for (var i = 3; i <= Math.sqrt(n); i += 2) {
          if (n % i == 0)
            return false;
        }
        return true;
      }
    }
    
    console.log(isPrime(977));