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

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?

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));

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

Related

Dynamic Programming: Implementing a solution using memoization

As the question states, I am trying to solve a leetcode problem. The solutions are available online but, I want to implement my own solution. I have built my logic. Logic is totally fine. However, I am unable to optimize the code as the time limit is exceeding for the large numbers.
Here's my code:
let count = 0;
const climbingStairs = (n, memo = [{stairs: null}]) => {
if(n === memo[n]) {
count += memo[n].stairs;
}
if(n < 0) return;
if(n === 0) return memo[n].stairs = count++;
memo[n] = climbingStairs(n - 1, memo) + climbingStairs(n - 2, memo);
return memo[n];
}
climbingStairs(20); //running fine on time
climbingStairs(40); //hangs as the code isn't optimized
console.log(count); //the output for the given number
The code optimization using the memoization object is not working. I have tried multiple ways but still, facing issues. Any help would be appreciated in optimizing the code. Thanks!
no need for count value, you can memoize this way:
const climbStairs = (n, memo = []) => {
if(n <= 2) return n;
if(memo[n]) {
return memo[n];
}
memo[n] = climbStairs(n - 1, memo) + climbStairs(n - 2, memo);
return memo[n];
}
Actually, you do not store a value, but NaN to the array.
You need to return zero to get a numerical value for adding.
Further more, you assign in each call a new value, even if you already have this value in the array.
A good idea is to use only same types (object vs number) in the array and not mixed types, because you need a differen hndling for each type.
const climbingStairs = (n, memo = [1]) => {
if (n < 0) return 0;
return memo[n] ??= climbingStairs(n - 1, memo) + climbingStairs(n - 2, memo);
}
console.log(climbingStairs(5));
console.log(climbingStairs(20));
console.log(climbingStairs(40));

How to reduce the time complexity of this problem?

After having a long difficult coding challenge, there was a problem that bugged me. I thought about it for an adequate time but couldn't find the way to solve it. Here, I am providing a problem and example below.
Input
v : an array of numbers.
q : 2 dimensional array with 3 elements in nested array.
Description
v is an array and q is a commands that does different thing according to its nested element.
if first element of nested array is 1 => second and third element of the nested array becomes the index and it returns sum[second:third+1] (As you can see, it is inclusive)
if first element of nested array is 2 => element of second index becomes the third. same as v[second] = third
Input example
v : [1,2,3,4,5]
q : [[1,2,4], [2,3,8], [1,2,4]]
Example
With a provided example, it goes like
command is [1,2,4] => first element is 1. it should return sum from v[2] to v[4] (inclusive) => 12.
command is [2,3,8] => first element is 2. it switches v[3] to 8. (now v is [1,2,3,8,5])
command is [1,2,4] => first element is 1. it should return sum from v[2] to v[4] (inclusive) => 16, as the third index has been changed from the previous command.
So the final answer is [12, 16]
Question.
The code below is how I solved, however, this is O(n**2) complexity. I wonder how I can reduce the time complexity in this case.
I tried making a hash object, but it didn't work. I can't think of a good way to make a cache in this case.
function solution(v, q) {
let answer = [];
for (let i = 0; i < q.length; i++) {
let [a, b, c] = q[i];
if (a === 1) {
let sum = 0;
for (let i = b; i <= c; i++) {
sum += v[i];
}
answer.push(sum);
} else if (a === 2) {
v[b] = c;
}
}
return answer;
}
This type of problem can typically be solved more efficiently with a Fenwick tree
Here is an implementation:
class BinaryIndexedTree extends Array {
constructor(length) {
super(length + 1);
this.fill(0);
}
add(i, delta) {
i++; // make index 1-based
while (i < this.length) {
this[i] += delta;
i += i & -i; // add least significant bit
}
}
sumUntil(i) {
i++; // make index 1-based
let sum = 0;
while (i) {
sum += this[i];
i -= i & -i;
}
return sum;
}
}
function solution(values, queries) {
const tree = new BinaryIndexedTree(values.length);
values.forEach((value, i) => tree.add(i, value));
const answer = [];
for (const [a, b, c] of queries) {
if (a === 1) {
answer.push(tree.sumUntil(c) - tree.sumUntil(b - 1));
} else {
tree.add(b, c - values[b]);
values[b] = c;
}
}
return answer;
}
let answer = solution([1,2,3,4,5], [[1,2,4], [2,3,8], [1,2,4]]);
console.log(answer);
Time Complexity
The time complexity of running tree.add or tree.sumUntil once is O(log๐‘›), where ๐‘› is the size of the input values (values.length). So this is also the time complexity of running one query.
The creation of the tree costs O(๐‘›), as this is the size of the tree
The initialisation of the tree with values costs O(๐‘›log๐‘›), as really each value in the input acts as a query that updates a value from 0 to the actual value.
Executing the queries costs O(๐‘šlog๐‘›) where ๐‘š is the number of queries (queries.length)
So in total, we have a time complexity of O(๐‘› + ๐‘›log๐‘› + ๐‘šlog๐‘›) = O((๐‘š+๐‘›)log๐‘›)
Further reading
For more information on Fenwick trees, see BIT: What is the intuition behind a binary indexed tree and how was it thought about?

reduce sum of digits recursivel down to a one digit number

I'm trying to solve a challenge (Digit Degree) on Code signal where the task is to find the the number of times we need to replace this number with the sum of its digits until we get to a one digit number. I.e. of the incoming number is 5 it's already a one digit number so the outcome should be 0. If the number is 100 the sum of its digits is 1 which is one digit so the outcome should be 1 and so on...
I'm doing a recursive solution like this:
let count = 0;
function digitDegree(n) {
if (n < 10) {
console.log(count);
return count;
};
const arr = n.toString().split('').map(Number);
const sumOfDigits = arr.reduce((acc, curr) => acc + curr);
count++;
digitDegree(sumOfDigits);
}
At the second loop and forth I'm getting null as output even if the console log shows the correct value. Where does it go wrong?
I saw that the question was up before but the answer was quite mathematical. Would this approach be ok or would it be considered bad practise?
You need a return statement for getting the result of the recursion.
return digitDegree(sumOfDigits);
A shorter approach would remove explicit conversions in advance and remove temporary arrays.
Then take another parameter for count and omit a global variable.
function digitDegree(n, count = 0) {
if (n < 10) return count;
return digitDegree(
n .toString()
.split('')
.reduce((acc, curr) => acc + +curr, 0),
count + 1
);
}
console.log(digitDegree(9999));
To return a value, update the last line in your function to return digitDegree(sumOfDigits)
function digitDegree(n) {
if (n < 10) {
console.log(count);
return count;
};
const arr = n.toString().split('').map(Number);
const sumOfDigits = arr.reduce((acc, curr) => acc + curr);
count++;
// add return
return digitDegree(sumOfDigits);
}
Pitfalls with your current approach:
It's impure.
digitDegree(100) returns 1 the first time you run it but returns 2 when you run it again. This is because count was declared outside the function (making it global)
let count = 0
function digitDegree(n) {
if (n < 10) {
return count;
};
const arr = n.toString().split('').map(Number);
const sumOfDigits = arr.reduce((acc, curr) => acc + curr);
count++;
return digitDegree(sumOfDigits);
}
// wrong - output should not change
console.log(digitDegree(100)) //=> 1
console.log(digitDegree(100)) //=> 2
console.log(digitDegree(100)) //=> 3
Make your function pure
A pure function is a specific kind of value-producing function that not only has no side effects but also doesnโ€™t rely on side effects from other codeโ€”for example, it doesnโ€™t read global bindings whose value might change.
A pure function has the pleasant property that, when called with the same arguments, it always produces the same value (and doesnโ€™t do anything else)
Source
Suggestions:
Pass count as an argument
function digitDegree(n,count=0) {
if (n < 10) {
return count;
};
const arr = n.toString().split('').map(Number);
const sumOfDigits = arr.reduce((acc, curr) => acc + curr);
count++;
return digitDegree(sumOfDigits, count);
}
// correct
console.log(digitDegree(100)) //=> 1
console.log(digitDegree(100)) //=> 1
console.log(digitDegree(100)) //=> 1
Wrap your recursion function inside another function
function recursionWrapper(num){
let count = 0;
function digitDegree(n) {
if (n < 10) {
return count;
};
const arr = n.toString().split('').map(Number);
const sumOfDigits = arr.reduce((acc, curr) => acc + curr);
count++;
// notice how we don't need a return in this approach
digitDegree(sumOfDigits)
}
digitDegree(num)
return count
}
// correct
console.log(recursionWrapper(100)) //=> 1
console.log(recursionWrapper(100)) //=> 1
console.log(recursionWrapper(100)) //=> 1
Further reading:
How to deal with dirty side effects in your pure functional JavaScript
Javascript and Functional Programming โ€” Pt. 3: Pure Functions

Multiplying digits within a number - excluding zeros

I have function taken from this example here that works well, except does not address any zeros that may be in the number, so everything is equaling zero when executing the function.
Multiplying individual digits in a number with each other in JavaScript
function digitsMultip(data) {
let arr = [];
for (let i of data) {
if (data[i] === 0) {
arr.push(data[i]);
}
}
return [...data.toString()].reduce((p, v) => p * v);
};
console.log(digitsMultip(3025));
I added to it a for-loop that accounts for the zero and remove it, but im doing something wrong here.
Uncaught TypeError: data is not iterable
DESIRED OUTPUT
3025 => 3 * 2 * 5 = 30
This iterates over the characters in your number. If the character is not "0" then it is added to the array. This array is then reduced by multiplying the values and then returned.
function digitsMultip(data) {
const arr = [];
for(let number of String(data)) {
if (number !== "0")
arr.push(number);
}
return arr.reduce((p, v) => p * v);
};
console.log(digitsMultip(3025));
You are getting that error because you are trying to iterate over a number.
Passing in a string or converting the number to string before iterating it would make it work.
Instead of looping it that way, a better and readable way would be to use the filter method to filter out the chars before multiplying:
function digitsMultip(data) {
return [...data.toString()].filter(n => n > '0').reduce((p, v) => p * v);
};
console.log(digitsMultip(3025));
Turn the input into a string, then split, filter the zeros and reduce multiplying
const input = 1203
const removeZeros = number =>{
const arr = number.toString().split('').filter(i => i !== '0')
return arr.reduce((a,c) => parseInt(a) * parseInt(c))
}
console.log(removeZeros(input))
One line version
const removeZeros = n => [...n.toString()].filter(c => c !== '0').map(x => parseInt(x)).reduce((a,c) => a*c)

Why is JS Map Function Returning Undefined? [duplicate]

This question already has answers here:
Why does JavaScript map function return undefined?
(13 answers)
Closed 1 year ago.
I have an array that is about 1000 in length. Why is map function returning undefined in certain indexes? Is there a way to only return array that meets this condition? I'd like to return an array with values > 0.
var total_percents = cars.map(function(element) {
var savings_percent = Number(element[0].getAttribute("percent-savings"));
if (savings_percent > 0)
return savings_percent;
});
You need to filter values after mapping, which you can do with filter array method with predicate like car => car > 0
var total_percents = cars.map((element) => Number(element[0].getAttribute("percent-savings"))).filter(car => car > 0)
You could also use reduce method for combining both operations at once:
var total_percents =
cars.reduce((acc, element) => {
const percentSavings = Number(element[0].getAttribute("percent-savings"));
if (percentSavings > 0) {
acc.push(percentSavings);
}
return acc;
}, [])
Unsure what you're attempting to accomplish, but:
Try something like this to return a sum of all pctSavings:
const totalPercents = cars.reduce((sum, el) => {
const pctSavings = +(el[0].getAttribute("percent-savings"));
if (pctSavings > 0) sum += pctSavings;
return sum;
}, 0);
To return an array of pctSavings, simply do this:
const totalPercents = cars.reduce((arr, el) => {
const pctSavings = +(el[0].getAttribute("percent-savings"));
if (pctSavings > 0) arr.push(pctSavings);
return arr;
}, []);
To get the max pctSavings do this:
let maxPctSavings = 0;
cars.forEach(el => {
const pctSavings = +(el[0].getAttribute("percent-savings"));
if (pctSavings > maxPctSavings) maxPctSavings = pctSavings
});
console.log(maxPctSavings) // this is your answer
Using reduce to both filter and map the value (should be faster and use less memory than separate filter and map)
var total_percents = cars.reduce(
function(out, element) {
var savings_percent = Number(element[0].getAttribute("percent-savings"));
if (savings_percent > 0) {
// Only add to the out-array if value is greater than 0.
out.push(savings_percent);
}
return out;
},
[] // start with empty array
);
total_percents will be an array, with values that is greater than 0.
Reading from a comment on another answer, what you wanted was the max savings.
Then I would do it like this:
var max_savings = cars.reduce(
function(value, element) {
var savings_percent = Number(element[0].getAttribute("percent-savings"));
return Math.max(savings_percent, value)
},
0 // start with no savings
);
"Number" will return "NaN" (Not a Number) if the argument cannot be converted to a number. Then you are using numeric comparison ">" of a string "NaN" to your zero value, so that comparison fails and does not return, therefore the "undefined" occurs. You could do a double comparison - if (typeof savings_percent == 'string'
if (typeof savings_percent == 'number' && savings_percent > 0)

Categories

Resources