One of my favorite algorithms is finding the digital root of any given integer. A digital root is a single-digit sum that is reached when you iteratively add up the digits that make up a number.
For example:
666
=> 6 + 6 + 6
=> 18
=> 1 + 8
=> 9
The key to solving this algorithm is to use an iterative method. The solution has to be smart enough to keep executing as long as the returned sum is higher than a single-digit number.
The approach
- If our given integer is greater than 9, iterate through each digit in the number.
- Add up each digit.
- Assess if the sum is a single-digit number.
- If not, go back to Step 1.
Let's break it down
1) Create a variable for the sum. Set it to equal the given integer. If this integer is a single-digit number, we'll return it at the very end without mutating it.
function digitalRoot(number) {
let sum = number
}
2) Write a conditional statement to execute something on the sum if it's a multi-digit number.
function digitalRoot(number) {
let sum = number
if (sum > 9) {
// execute something here
}
}
3) If the number is greater than 9, turn it into an array so that we can loop through it. In JavaScript, we have to turn the integer into a string, and then call the split()
method on it to achieve this.
function digitalRoot(number) {
let sum = number
let arr = []
if (sum > 9) {
arr = sum.toString().split("")
console.log(arr)
}
}
digitalRoot(24)
=> ["2", "4"]
4) Now let's iterate through the array and sum its elements. We can use the reduce()
method for this. reduce()
requires a reducer method to execute on, so let's write the logic for it and pass it into reduce()
. Inside the reducer method, convert the values into an integer by wrapping each value in parseInt
. Since this method will return a single value, we can reassign it to our sum
variable.
function digitalRoot(number) {
let sum = number
let arr = []
let reducer = (a,b) => parseInt(a) + parseInt(b)
if (sum > 9) {
arr = sum.toString().split("")
sum = arr.reduce(reducer)
console.log(sum)
}
}
digitalRoot(24)
=> 6
Et voilà! We've solved the algorithm!
...Just kidding. It totally breaks if we pass in any larger numbers.
digitalRoot(666)
=> 18
So how can we keep executing our function while the sum is a multi-digit number?
5) Instead of a conditional if statement, let's use a while loop. The while loop will run while a condition is true, whereas an if statement will just execute once. Let's also move our console.log
statement to the end of the function, outside of the loop, so that it only returns a single value.
function digitalRoot(number) {
let sum = number
let arr = []
let reducer = (a,b) => parseInt(a) + parseInt(b)
while (sum > 9) {
arr = sum.toString().split("")
sum = arr.reduce(reducer)
}
console.log(sum)
}
digitalRoot(666)
=> 9
Conclusion
This is one of my favorite algorithms because it's not the most difficult to solve, but still poses an interesting problem. Sound off in the comments if you have a different way of solving this!
Edits made after publishing
Changed the wording in this article because I originally talked about using a recursive solution, but ended up writing just an iterative one. Thanks for all the comments! ✨
Top comments (14)
Very cool! I'm a Ruby enthusiast so I tried it out in that.
Your Iterative strategy was great but I think using recursion could help reduce some of the complexity and make for some cleaner code.
Ruby gives us some great helpers for integers so we can simplify this even more.
Nice and simple!
Can I cheat?
:D
I am really curious how do you know this?
I learnt it first as a math trick for children in Vietnam: to know if something is divisible by 9, you calculate its digitial root and see if the digital root is divisible by 9 (same goes for 3).
When I grew up, I was curious and tried to prove that mathematically, which is how I ended up with digitalRoot(num) = num % 9 (or 9 if divisible by 9) :D
mathworld.wolfram.com/DigitalRoot....
Looking good in this corner of the thread!
What about d_root(0) ?
Amazing intuition!
Are we sure this is a recursive approach? I feel like this is an iterative solution as we just run a block until a condition is met. The function is not called multiple times.
I think this qualifies as recursive. But the iterative approach in the article is much more performant I think. Nice article.
Thank you! I like those tiny little programming "snacks" :)
shortest (recursion) solution I could find:
Cool demo, but that's not actually recursion, that's just iteration. An actual recursive solution, if you wanted to do that for some strange reason, would look more like
Also, sidenote, this function does break on very large numbers. That's not OP's fault, that's because Javascript can't reasonably represent things like 1234567891234567891234567891 as an integer.
digitalRoot("1234567891234567891234567891")
works just fine, thoughfunction F_iRoot(P_sz0){
var sz0 = "" + P_sz0;
var iLen = sz0.length;
var iNum;
var ch;
var iSum = 0;
for (var i=0; i < iLen; i++) {
ch = sz0.charCodeAt(i);
iNum = ch - 48; // '0' = 0x30 = 48
iSum += iNum;
} /* for /
if (iSum > 9) {
iSum = F_iRoot(iSum);
} / if /
return(iSum);
} / F_iRoot */
I love this post! You explain it all so well! Are you planning on creating an algo series?
thank you so much omg
The better solution I think:
function digital_root(n) {
return (n - 1) % 9 + 1;
}