If you are just trying to count how many times it gets reduced and are not caring about the recursion specifically... you can just remove the recursion. The below code remains faithful to the Original Post as it does not count num <= 9
as needing reduction. Therefore, singleDigit(8)
will have count = 0
, and singleDigit(39)
will have count = 3
, just like the OP and accepted answer are demonstrating:
const singleDigit = (num) => {
let count = 0, ret, x;
while (num > 9) {
ret = 1;
while (num > 9) {
x = num % 10;
num = (num - x) / 10;
ret *= x;
}
num *= ret;
count++;
console.log(num);
}
console.log("Answer = " + num + ", count = " + count);
return num;
}
It is unnecessary to process numbers 9 or less (ie. num <= 9
). Unfortunately the OP code will process num <= 9
even tho it does not count it. The code above will not process nor count num <= 9
, at all. It just passes it thru.
I choose not to use .reduce
because doing the actual math was much faster to execute. And, for me, easier to understand.
I feel good code is also fast. If you are using this type of reduction (which is used in numerology a lot) you might be needing to use it on a massive amount of data. In this case, speed will become the upmost of importance.
Using both .map(Number)
and console.log
(at each reduction step) are both very very long to execute and unnecessary. Simply deleting .map(Number)
from the OP sped it up by about 4.38x. Deleting console.log
sped it up so much it was almost impossible to properly test (I didn't want to wait for it).
So, similar to customcommander's answer, not using .map(Number)
nor console.log
and pushing the results into an array and using .length
for count
is much much faster. Unfortunately for customcommander's answer, using a generator function is really really slow (that answer is about 2.68x slower than the OP without .map(Number)
and console.log
)
Also, instead of using .reduce
I just used the actual math. This single change alone sped up my version of the function by a factor of 3.59x.
Finally, recursion is slower, it takes up stack space, uses more memory, and has a limit to how many times it can "recur". Or, in this case, how many steps of reduction it can use to finish the full reduction. Rolling out your recursion to iterative loops keeps it all on the same place on the stack and has no theoretical limit on how many reduction steps it can use to finish. Thus, these functions here can "reduce" almost any sized integer, only limited by execution time and how long an array can be.
All this in mind...
const singleDigit2 = (num) => {
let red, x, arr = [];
do {
red = 1;
while (num > 9) {
x = num % 10;
num = (num - x) / 10;
red *= x;
}
num *= red;
arr.push(num);
} while (num > 9);
return arr;
}
let ans = singleDigit2(39);
console.log("singleDigit2(39) = [" + ans + "], count = " + ans.length );
// Output: singleDigit2(39) = [27,14,4], count = 3
The above function runs extremely fast. It is about 3.13x faster than the OP (without .map(Number)
and console.log
) and about 8.4x faster than customcommander's answer. Keep in mind that deleting console.log
from the OP prevents it from producing a number at each step of reduction. Hence, the need here to push these results into an array.
PT