Nesting ES6 array helper methods to generate an array of prime numbers

Question

So I wanted to write a function that returns the sum of all prime numbers up to and including a provided number.

I have written this, which works:

function sumPrimes(num) {
  const arr = Array.from({length: num+1}, (v, k) => k).slice(2);
  return arr.filter(element => {
  	for(let i = 2; i < element; i++) {
          if(element % i === 0) {
              return false;
          }
      }
      return element;
  }).reduce((previous, current) => {
  	return previous += current;
  }, 0);
}

sumPrimes(9);

I was thinking it would look much neater if the for loop was replaced with another array helper method. I'm struggling with the implementation of this however.

This is what I've got so far:

function sumPrimes(num) {
  const arr = Array.from({length: num+1}, (v, k) => k).slice(2);
  return arr.filter(element => {
  	return arr.find(ref => {
      console.log("(" + element + " % " + ref + " === 0) " + (element % ref === 0));
    	if(element % ref === 0) { return false; }
      return true;
    });
  }).reduce((previous, current) => {
  	return previous += current;
  }, 0);
}

sumPrimes(20);

Written like this, the function no longer works as expected – it doesn't filter any of the numbers so all are summed by the .reduce helper. The console makes it appear like the if statement is still working as desired; what am I doing wrong?


Show source
| javascript   | arrays   | ecmascript-6   | primes   | helpers   2017-01-02 16:01 2 Answers

Answers ( 2 )

  1. 2017-01-02 17:01

    The reason your code doesn't work using find is because find is not a proper replacement for your for loop. The for loop you have here returns a boolean indicating if a divisor is found. find on the other hand, returns the divisor itself. This means all conditions for your filter method are numbers above 1, which all evaluate as truthy and hence nothing gets filtered.

    The more appropriate method for your use case would be some or every. These essentially work like find, except return a boolean as soon as they find an element satisfying the conditions.

    • some stops and returns true as soon as the predicate function returns true for some element. Otherwise it returns false.

    • every stops and returns false as soon as the predicate function returns false for some element. Otherwise it returns true.

    One more issue would be that using a helper like this makes your code less efficient because you're now checking all numbers and not just up to the current number. This means your predicate function must include this equality check as well, or you'll have to first filter the array for all elements bellow the element being checked.

    Another small improvement in terms of efficiency is that you don't need to iterate all the way up to element - 1 to find a divisor. Iterating up to sqrt(element) is enough because all numbers higher than sqrt(element) that divide element will allready have a complement divisor somewhere bellow sqrt(element).

    Here's an approach using every and filtering the elements bellow the square root of the element being checked.

    function sumPrimes(num) {
      const arr = Array.from({length: num+1}, (v, k) => k).slice(2);
      return arr.filter(element => {
        return arr
          .filter(ref => ref*ref <= element)  // filter elements less than sqrt(current)
          .every(ref => element % ref !== 0); // filter elements that have a divisor
      }).reduce((previous, current) => {
        return previous += current;
      }, 0);
    }
    
    console.log(sumPrimes(9)); // 17

    Perhaps a less functional but more efficient (and IMHO equally clean) way would be to just convert your for loop into a helper function:

    function isPrime(element) {
      for(let i = 2; i*i <= element; i++) {
        if(element % i === 0) {
          return false;
        }
      }
      return true;
    }
    
    function sumPrimes(num) {
      return Array
        .from({ length: num+1 }, (v, k) => k)
        .slice(2)
        .filter(isPrime)
        .reduce((previous, current) => previous += current, 0);
    }
    
    console.log(sumPrimes(9)); // 17

  2. 2017-01-02 17:01

    You can reduce the range of research for primality of n at sqrt(n) :

    var isPrime = n => n===2 ? true : Array(Math.ceil(Math.sqrt(n))+1).fill().map((e,i)=>i).slice(2).every(m => n%m);
     
    var sumPrimes = num => Array(num).fill().map((e,i)=>i+1).slice(1).filter(isPrime).reduce((a,b) => a+b);
    
    console.log(sumPrimes(9));

◀ Go back