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?
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