continually add values in javascript with while loop - javascript

I'm a newbie to Javascript so please bear with me for this basic question,
I'm trying to get my function to add all the individual digits in a string together, and then keep doing this until I'm left with a single digit!
3253611569939992595156
113 // result of the above digits all added together
5 //result of 1+1+3
I've created a while loop, but it only adds the numbers together once, it dosn't repeat until a single digit and I can't work out why!
function rootFunc(n) {
var splite = n.toString().split('').map(x => Number(x)); //converts the number to a string, splits it and then converts the values back to a number
while (splite.length > 1) {
splite = splite.reduce(getSum);
}
return splite;
}
console.log(rootFunc(325361156993999259515));
function getSum(total, num) {
return total + num;
}

You're reducing properly, but what you're not doing is re-splitting. Try breaking this out into separate functions:
function digits(n) {
return n.toString().split('').map(x =>Number(x));
}
Then split each time:
function rootFunc(n) {
var d = digits(n);
while (d.length > 1) {
d = digits(d.reduce(getSum));
}
return d;
}

The problem here is that you return the result after the first splice. You need to have a recursive function. To do this, you can put this before the return :
if(splite > 9) splite = rootFunc(splite);
This way, you check if the result is greater than 10, if not you do the function with the remaining digits

I was looking this over in jsfiddle, and your number isn't being passed to exact precision, so just console logging n as soon as you call rootFunc, you've already lost data. Otherwise, to fix your loop, you need to remap splite to a string before the end of your codeblock since your while statement is checking .length, which needs to be called on a string. Put this piece of code at the end of the block:
splite = splite.toString().split('').map(x =>Number(x));

Related

How to sum digits recursively javascript

I'm learning the basics of JavaScript and am trying to write a recursive function to add together a group of integers. For example, the function argument would be 1234 and the result should be 10. Here's what I have so far...
function sumDigits(numbersStr) {
var numToString = numbersStr.toString()
var numArr = numToString.split('').map(Number);
var sum = 0;
// base case
if (numArr.length === 0) {
return sum
}
// recursive case
else {
var popped = numArr.pop();
sum+=popped;
return sumDigits(numArr);
}
}
But I get an infinite loop whenever I run this (my tab crashes). If I'm popping the last element of an array, adding it to the sum variable, then calling the function again on the shortened array, then why do I get an infinite loop? Many thanks!
The problem in your code is that sumDigits expects to get a number, but in the recursion you pass an array of numbers to it.
You could use a string or number as argument of the function and convert the value to a string.
Then check the length and return zero if the length is zero (usualy the exit condition).
If not return the value of the fist character and add the result of calling the function with a sliced string from index one.
Basically a recurisve function have two parts.
The exit condition with an exit value. This depends on the purpose of the recursive function. It is usually a neutral value, like zero for addition, or 1 for multiplication.
The actuall value plue a arithmetic operation and the call of the function again with a reduced string/array or numerical value.
function sumDigits(num) {
num = num.toString();
return num.length === 0
? 0
: +num[0] + sumDigits(num.slice(1));
}
console.log(sumDigits(1234));
Another approach would be the use of tail recursion, which does not extend the stack for each calling function, because the function ends with the call without keeping a temporary value liek in above function, the actual numerical value +num[0] and the waiting for execution of the adition.
In this case, you could store the intermediate result along with the calling of the function.
function sumDigits(num, sum) { // num is expected to be a string
sum = sum || 0;
if (num.length === 0) {
return sum;
}
return sumDigits(num.slice(1), sum + +num[0]);
}
console.log(sumDigits('1234'));
function digitCount(num) {
let val1 = num % 10
let rem = Math.floor(num / 10)
return val1 + (rem != 0 ? digitCount(rem) : 0)
}
console.log(digitCount(87))
The problem is that your function takes a number as it's argument, but when you use it recursively, you're giving it an array back. I would recommend pulling the recursive part into its own helper function like so:
function sumDigits(num) {
var numStr = num.toString()
var numArr = numStr.split('').map(Number);
function sumHelper(arr) {
// Base case:
if (arr.length === 1) {
return arr[0];
}
// Otherwise recurse (return last number plus the sum
// of the remainder):
return arr.pop() + sumHelper(arr);
}
// Then use that recursive helper:
return sumHelper(numArr);
}
console.log(sumDigits(1234))
Your function expects a string, but on the recursive call, you pass it an array.
Additionally, you've got a call to .map that isn't needed because you can convert the strings in the .split array to numbers simply by prepending a + to them.
Is there any reason you just don't use Array.reduce?
function sumDigits(stringOfNums) {
// Split the string into an array of strings. Reduce the array to the sum by
// converting each string to a number.
console.log(stringOfNums.split('').reduce(function(x,y){ return +x + +y}, 0));
}
sumDigits("1234");
Passing an array in the recursive call will guarantee that its .toString() will never be empty because the commas will add more characters than have been removed.
Instead, do it mathematically so you don't need an array or even string conversion.
function sumDigits(num) {
return num ? (num%10) + sumDigits(Math.floor(num/10)) : 0
}
console.log(sumDigits(1234))
This assumes a positive integer is passed. You'll need additional guards if other input could be provided.
There's no need to convert the number to an array. You can get the last digit of a number with number % 10, and remove that digit with Math.floor(number / 10). Then recurse until the number is 0.
function sumDigits(num) {
if (num == 0) {
return 0;
} else {
var last = num % 10;
var rest = Math.floor(num / 10);
return last + sumDigits(rest);
}
}
console.log(sumDigits(1234));
Barmar has a good voice of reason. Converting from number to string then converting back to number again is a bit silly. Plus, if this is a homework assignment, using high-level functions like String.prototype.split probably won't teach you much.
Here's a tail-recursive version Barmar's program written using functional style
base case - the input number n is zero, return the accumulator acc
inductive case - n is not zero; recur with the next n and the next acc
const sumDigits = (n = 0, acc = 0) =>
n === 0
? acc
: sumDigits (n / 10 >> 0, acc + n % 10)
console.log (sumDigits ()) // 0
console.log (sumDigits (1)) // 1
console.log (sumDigits (12)) // 3
console.log (sumDigits (123)) // 6
console.log (sumDigits (1234)) // 10

Why is the last element of my array being replaced with a 0

I have a function:
function splitToDigits(n) {
var digits = ("" + n).split("").map(function(item) {
return parseInt(item, 10);
});
console.log(digits);
}
console.log(splitToDigits(123456784987654321));
This is returning digits = [1,2,3,4,5,6,7,8,4,9,8,7,6,5,4,3,2,0].
Any idea why the last element is 0? I noticed that when I delete 2 elements from the array it acts normally. Thanks for all the great answers! :)
As Jaromanda X mentioned in the comments section above, JavaScript does not have enough precision to keep track of every digit in the integer you passed to your function.
To fix this problem, you should instead pass a string:
console.log(splitToDigits('123456784987654321'))
However, I would also like to point out that you can greatly simplify your splitToDigits method:
function splitToDigits(n) {
return [].map.call(n + '', Number)
}
console.log(splitToDigits('123456784987654321'))
console.log(splitToDigits(1234))
It's because Javascript is truncating numbers.
The best way to see this is by doing this console.log:
function splitToDigits(n) {
console.log(n);
var digits = ("" + n).split("").map(function(item) {
return parseInt(item, 10);
});
}
Then, when you ran: splitToDigits(123456784987654321), you already get 123456784987654320. Hence, it has nothing to do with your code as you have still not processed it.
If you add digits, it changes to scientific notation:
splitToDigits(1234567849876543211521521251) // turns to 1.2345678498765432e+27
It's a Javascript precision issue. That's all :)

How can I "break up" numbers into smaller pieces in javascript?

I think the title needs some explaining. I wan't to make my program break up a number into smaller bits.
For example, it would break 756 into 700, 50 and 6. 9123 would be 9000, 100, 20 and 3. Is there any way I can do this for any reasonably sized number?
Working Example
Here is a function that can do it:
function breakNumbers(num){
var nums = num.toString().split('');
var len = nums.length;
var answer = nums.map(function(n, i) {
return n + (Array(len - i - 1).fill(0)).join('');
});
return answer.map(Number).filter(function(n) {return n !== 0;});
}
function breakup(number) {
var digits = String(number).split('')
return digits.map(function(digit, i) {
return Number(digit.concat("0".repeat(digits.length - i - 1)))
}).filter(function(n) { return n !== 0 })
}
So first, we want to cast the number to a string, so we pass it into the String primitive like so: String(number)
Thus, calling the split method on the array and passing in an empty string (which tells it to split for every character) results in an array of the digits, i.e. ["7", "5", "6"]
We can leave them as strings for now because it makes the next part a little easier. Using the map function, you can pass a function which should be called on each element in the array. Besides the first argument to this function, there's an optional second argument which is the index of the item in the array. This will turn useful in our case, since where a number is in the array indicates what place it is.
Check it out, the value returned by the function passed to map takes the current number string and concats another string onto it, which is a number of repeated "0"s. That number is determined by looking at the parent array's length and subtracting it from the index of the current item being looped on, minus one. This is because arrays are 0-indexed in JavaScript--if we just subtracted digits.length from the i (index) for the first iteration, the values would be 3 and 0 respectively, so you'd end up with 7000 for the first value if you passed in 756. Note also that in our return statement inside the map function, we wrap it back in a Number primitive to cast it back from a string.
Also, you didn't mention this, but I assume you'd rather not have numbers which equal 0 in your example. By calling filter on the final array before its returned, we can effectively make sure that only items which are not equal to 0 are returned. Thus, if you call breakup(756) you'll recieve [700, 50, 6], but breakup(706) will give you [700, 6] instead.
Instead of using split() to break out digits, I used a regex to tokenize the number string. This way, we can easily handle any trailing decimals by treating a digit followed by a decimal point and any further digits as a single token. This also makes it possible to handle digits as part of a larger string.
function splitNumber( number ) {
var parts = [];
var re = /(\d(?:\.\d*)?)/g;
while(next_part = re.exec(number)) {
// adjust place value
parts.forEach( function(element, index) {
parts[index] = 10 * element;
} );
parts.push( next_part[0] );
}
return parts.map(Number).filter(function(n) {return n !== 0});
}

How to subtract from a number contained in a string?

I have a string that contains a number, eg,
images/cerberus5
The desired result
images/cerberus4
How can I subtract 1 from the '5' in the first string to obtain the '4' in the second?
This is a raw example, but you could do something like this:
$old_var = 'images/cerberus4';
$matches = [];
$success = preg_match_all('/^([^\d]+)(\d+)$/', $old_var, $matches);
$new_val = '';
if (isset($matches[2]) && $success) {
$new_val = $matches[2][0].((int)$matches[2][0] + 1);
}
It's not meant to be the perfect solution, but just to give a direction of a possible option.
What the RegEx doesn't detect (because it's more strict) is that it won't work without a trailing number (like images/cerberus), but as it seems an 'expected' pattern I also wouldn't allow the RegEx to be more loose.
By putting this code into a function or class-method you could add a parameter to automatically be able to tell the code to add, subtract or do other modifications to the trailing number.
function addOne(string){
//- Get first digit and then store it as a variable
var num = string.match(/\d+/)[0];
//- Return the string after removing the digits and append the incremented ones on the end
return (string.replace(/\d+/g,'')) + (++num);
}
function subOne(string){
var num = string.match(/\d+/)[0];
//- Same here just decrementing it
return (string.replace(/\d+/g,'')) + (--num);
}
Don't know if this is good enough but this is just two functions that return the string. If this has to be done via JavaScript so doing:
var test = addOne("images/cerberus5");
Will return images/cerberus6
and
var test = subOne("images/cerberus5");
Will return images/cerberus4

Recursion and Loops - Maximum Call Stack Exceeded

I'm trying to build a function that adds up all the numbers within a string... for example, 'dlsjf3diw62' would end up being 65.
I tried to be clever and put together a recursive function:
function NumberAddition(str) {
var numbers='1234567890';
var check=[];
str=str.split[''];
function recursive(str,check) {
if (str.length==0)
return check;
else if (numbers.indexOf(str[0])>=0)
{
for (i=0;i<str.length;i++){
if (numbers.indexOf(str[i])<0)
check.push(str.slice(0,i));
str=str.slice(i);
return recursive(str,check);
}
}
else
str.shift();
return recursive(str,check);
}
You'll see that I'm trying to get my numbers returned as an array in the array named check. Unfortunately, I have a maximum call stack size exceeded, and I'm not sure why! The recursion does have a base case!! It ends once str no longer has any contents. Why wouldn't this work? Is there something I'm missing?
-Will
You can achieve the same thing with a far easier solution, using regular expressions, as follows:
var str = 'dlsjf3diw62';
var check = str.match(/\d+/g); // this pattern matches all instances of 1 or more digits
Then, to sum the numbers, you can do this:
var checkSum = 0;
for (var i = 0; i < check.length; i++) {
checkSum += parseInt(check[i]);
}
Or, slightly more compact:
var checkSum = check.reduce(function(sum, num){ return sum + parseInt(num) }, 0);
The reason your recursion doesn't work is the case where you do enter the for loop, because you've found a digit, but the digits continue to the end of the string. If that happens, the return inside the for loop never happens, and the loop ends. After that, the .shift() does not happen, because it's in that else branch, so you return re-process the same string.
You shouldn't solve this particular problem that way, but the code makes a good example of the anti-pattern of having return statements inside if bodies followed by else. Your code would be clearer (and would work) if it looked like this:
function recursive(str, check) {
if (str.length == 0)
return check;
if (numbers.indexOf(str[0]) >= 0) {
// Find the end of the string of digits, or
// the end of the whole thing
for (var i = 0; i < str.length && numbers.indexOf(str[i]) >= 0; i++);
check.push(str.slice(0, i));
str = str.slice(i);
return recursive(str, check);
}
// A non-digit character
str.shift();
return recursive(str, check);
}
In that version, there are no else clauses, because the two if clauses always involve a return. The for loop is changed to simply find the right value of "i" for the subsequent slicing.
edit — one thing this doesn't fix is the fact that you're pushing arrays into your "check" list. That is, the substring "62" would be pushed as the array ["6", "2"]. That's not a huge problem; it's solved with the addition of a .join() in the right place.

Categories