reduce sum of digits recursivel down to a one digit number - javascript

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

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

Can't detect the cause of infinite loop in a 'while loop' in JS

I've got an infinite loop inside my while loop and I can't find the cause.
It's a simple function that returns the sum of the argument's digits. I use a while loop because it needs to add up the digits until it lands at a one digit number.
I made sure that I added a statement that would make sure that at a certain point the loop will break., But it obviously doesn't.
function digital_root(n) {
num = n;
sum = 0;
while (num.toString().length>1){
for (i=0; i<num.toString().length; i++) {
sum += parseInt(num.toString()[i])
}
num = sum;
}
return sum;
}
digital_root(456)
I get a warning that I have an infinity loop on line 4 (while loop).
I hoped that num=sumwould reassign the new integer (with reduced number of digits) to the numvariable and thus at some point break out of the loop. Is this wrong?
What further confuses me is that most of the JS editors I've used to debug the problem return an output, but it takes ages. So is it an infinite loop or is it not?
You have a nested loop structure where the first condition is always true.
For getting only a number below 10, you could call the function again as recursion.
function digital_root(n) {
var num = n.toString(), // declare and use the string value
sum = 0,
i;
for (i = 0; i < num.length; i++) {
sum += parseInt(num[i], 10)
}
return sum > 9
? digital_root(sum)
: sum;
}
console.log(digital_root(456));
After re-reading the question I noticed that you're trying to reduce an integer down to a single number. The issue with your code was that sum was set to 0, only before the while loop. Meaning that it didn't reset for the second, third, ... run.
Moving sum = 0 into the while loop resolves this issue. I've also added variable declarations at the top to avoid setting global variables.
function digital_root(n) {
var num, sum, i;
num = n;
while (num.toString().length > 1) {
sum = 0;
for (i = 0; i < num.toString().length; i++) {
sum += parseInt(num.toString()[i]);
}
num = sum;
}
return sum;
}
console.log(digital_root(456));
Here written in a recursive manner, a style that I personally more prefer:
function digital_root(integer) {
// guard against things that might result in an infinit loop, like floats
if (!Number.isInteger(integer) || integer < 0) return;
const chars = integer.toString().split("");
if (chars.length == 1) return integer;
return digital_root(
chars.map(char => parseInt(char))
.reduce((sum, digit) => sum + digit)
);
}
console.log(digital_root(456));
Since you already got the answer, here is another way to meet the result
function digital_root(n) {
// convert the number to string
// use split to create an array of number viz ['4','5','6']
// use reduce to sum the number after converting to number
// 0 is the initial value
return n.toString().split('').reduce((a, c) => a + parseInt(c, 10), 0)
}
console.log(digital_root(456))
Avoiding all the nested loops that lead to a situation such as that you're facing, I'd rather do it in a more readable way.
function digital_root(n) {
sum = n;
while(sum.toString().length > 1) {
sum = sum.toString()
.split('')
.map(digit => parseInt(digit, 10))
.reduce((acc, cur) => acc + cur);
}
return sum;
}
console.log(digital_root(456));

I need to extract every nth char of a string in Javascript

Ive been reading everything online but its not exactly what I need
var x = 'a1b2c3d4e5'
I need something to get me to
using 1 the answer should be abcde
using 2 the answer should be 12345
using 3 the answer should be b3e
the idea behind it if using 1 it grabs 1 skips 1
the idea behind it if using 2 it grabs 2 skips 2
the idea behind it if using 3 it grabs 3 skips 3
I dont want to use a for loop as it is way to long especially when your x is longer than 300000 chars.
is there a regex I can use or a function that Im not aware of?
update
I'm trying to some how implement your answers but when I use 1 that's when I face the problem. I did mention trying to stay away from for-loops the reason is resources on the server. The more clients connect the slower everything becomes. So far array.filter seem a lot quicker.
As soon as I've found it I'll accept the answer.
As others point out, it's not like regular expressions are magic; there would still be an underlying looping mechanism. Don't worry though, when it comes to loops, 300,000 is nothing -
console.time('while')
let x = 0
while (x++ < 300000)
x += 1
console.timeEnd('while')
// while: 5.135 ms
console.log(x)
// 300000
Make a big string, who cares? 300,000 is nothing -
// 10 chars repeated 30,000 times
const s =
'abcdefghij'.repeat(30000)
console.time('while string')
let x = 0
let interval = 2
let values = []
while (x < s.length)
{ values.push(s[x])
x += interval
}
let result = values.join('')
console.timeEnd('while string')
// while string: 31.990ms
console.log(result)
console.log(result.length)
// acegiacegiacegiacegiacegiacegiacegiacegia...
// 150000
Or use an interval of 3 -
const s =
'abcdefghij'.repeat(30000)
console.time('while string')
let x = 0
let interval = 3
let values = []
while (x < s.length)
{ values.push(s[x])
x += interval
}
let result = values.join('')
console.timeEnd('while string')
// while string: 25.055ms
console.log(result)
console.log(result.length)
// adgjcfibehadgjcfibehadgjcfibehadgjcfibe...
// 100000
Using a larger interval obviously results in fewer loops, so the total execution time is lower. The resulting string is shorter too.
const s =
'abcdefghij'.repeat(30000)
console.time('while string')
let x = 0
let interval = 25 // big interval
let values = []
while (x < s.length)
{ values.push(s[x])
x += interval
}
let result = values.join('')
console.timeEnd('while string')
// while string: 6.130
console.log(result)
console.log(result.length)
// afafafafafafafafafafafafafafafafafafafafafafa...
// 12000
You can achieve functional style and stack-safe speed simultaneously -
const { loop, recur } = require('./lib')
const everyNth = (s, n) =>
loop
( (acc = '', x = 0) =>
x >= s.length
? acc
: recur(acc + s[x], x + n)
)
const s = 'abcdefghij'.repeat(30000)
console.time('loop/recur')
const result = everyNth(s, 2)
console.timeEnd('loop/recur')
// loop/recur: 31.615 ms
console.log(result)
console.log(result.length)
// acegiacegiacegiacegiacegiacegiacegia ...
// 150000
The two are easily implemented -
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let acc = f()
while (acc && acc.recur === recur)
acc = f(...acc.values)
return acc
}
// ...
module.exports =
{ loop, recur, ... }
And unlike the [...str].filter(...) solutions which will always iterate through every element, our custom loop is much more flexible and receives speed benefit when a higher interval n is used -
console.time('loop/recur')
const result = everyNth(s, 25)
console.timeEnd('loop/recur')
// loop/recur: 5.770ms
console.log(result)
console.log(result.length)
// afafafafafafafafafafafafafafa...
// 12000
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let acc = f()
while (acc && acc.recur === recur)
acc = f(...acc.values)
return acc
}
const everyNth = (s, n) =>
loop
( (acc = '', x = 0) =>
x >= s.length
? acc
: recur(acc + s[x], x + n)
)
const s = 'abcdefghij'.repeat(30000)
console.time('loop/recur')
const result = everyNth(s, 2)
console.timeEnd('loop/recur')
// loop/recur: 31.615 ms
console.log(result)
console.log(result.length)
// acegiacegiacegiacegiacegiacegiacegia ...
// 150000
Since I'm not an expert of regex, I'd use some fancy es6 functions to filter your chars.
var x = 'a1b2c3d4e5'
var n = 2;
var result = [...x].filter((char, index) => index % n == 0);
console.log(result);
Note that because 0 % 2 will also return 0, this will always return the first char. You can filter the first char by adding another simple check.
var result = [...x].filter((char, index) => index > 0 && index % n == 0);
As a variant:
function getNth(str, nth) {
return [...str].filter((_, i) => (i + 1) % nth === 0).join('');
}
console.log(getNth('a1b2c3d4e5', 2)); // 12345
console.log(getNth('a1b2c3d4e5', 3)); // b3e
What I'd suggest, to avoid having to iterate over the entire array, is to step straight into the known nth's.
Here's a couple of flavors:
function nthCharSubstr(str, nth) {
let res = "";
for (let i = nth - 1; i < str.length; i += nth) {
res += string[i];
}
return res;
}
More ES6-y:
const nthCharSubstr = (str, nth) =>
[...Array(parseInt(str.length / nth)).keys()] // find out the resulting number of characters and create and array with the exact length
.map(i => nth + i * nth - 1) // each item in the array now represents the resulting character's index
.reduce((res, i) => res + str[i], ""); // pull out each exact character and group them in a final string
This solution considers this comment as being valid.

Clearing a counter after each function call: JavaScript Recursive function

I have the folllowing solution to a problem relating to multiplicative persistence. However, I need to wipe the counter after each function call.
I have tried different return statements, counters and arrays.
I don't seem to be able to clear the counter after each function call AND
get the correct answer. It is adding all the answers from multiple function calls.
function persistence(num, counter = 0) {
if (num.toString().length != 1) {
num = num.toString().split("").filter(Number).reduce((a, b) => a * b);
persistence(num, ++counter);
}
return counter;
}
persistence(999) // Answer should be 4.
persistence(25)// Answer should be 2 not 6 or 1.
The tests here:
describe('Initial Tests', function () {
Test.assertEquals(persistence(39),3);
Test.assertEquals(persistence(4),0);
Test.assertEquals(persistence(25),2);
Test.assertEquals(persistence(999),4);
});
You need to return the result of each recursive call and handle the else case.
Try this:
function persistence(num, counter = 0) {
if (num.toString().length != 1) {
num = num.toString().split("").filter(Number).reduce((a, b) => a * b);
return persistence(num, ++counter);
} else {
return counter;
}
}
Here are the results from console:
> persistence(25)
< 2
> persistence(999)
< 4
I'm assuming you're trying to compute multiplicative digital root but that does not remove zeroes from the computation as you're doing with .filter(Number) above. Below, we write multiplicativeRoot which returns an array of the steps it takes to reduce a number to a single digit
Finally, the multiplicative persistence can be computed by simply counting the number of steps in the return value from multiplicativeRoot and subtracting 1 (the first value in the result is always the input value)
The result is an implementation of multiplicativePersistence that is made up of several functions, each with their distinct and clear purpose
const digits = n =>
n < 10
? [ n ]
: digits (n / 10 >> 0) .concat ([ n % 10 ])
const mult = (x,y) =>
x * y
const product = xs =>
xs.reduce (mult, 1)
const multiplicativeRoot = x =>
x < 10
? [ x ]
: [ x ] .concat (multiplicativeRoot (product (digits (x))))
const multiplicativePersistence = x =>
multiplicativeRoot (x) .length - 1
console.log (multiplicativeRoot (999)) // [ 999, 729, 126, 12, 2 ]
console.log (multiplicativePersistence (999)) // 4
console.log (multiplicativeRoot (25)) // [ 25, 10, 0 ]
console.log (multiplicativePersistence (25)) // 2

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

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

Categories

Resources