Multiplicative Persistence Codewars Challenge - javascript

I've been working on a kata from Codewars, the challenge is to write a function, persistence, that takes in a positive parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num until you reach a single digit.
Example:
persistence(39) === 3 // because 3*9 = 27, 2*7 = 14, 1*4=4
// and 4 has only one digit
persistence(999) === 4 // because 9*9*9 = 729, 7*2*9 = 126,
// 1*2*6 = 12, and finally 1*2 = 2
persistence(4) === 0 // because 4 is already a one-digit number
While trying to figure this out I came across a solution online (shown below) and after trying to understand its logic, I couldn't see why the code didn't work
var count = 0;
function persistence(num) {
if (num.toString().length === 1) {
return count;
}
count++;
var mult = 1;
var splitStr = num.toString().split("");
for (var i = 0; i <= splitStr; i++) {
mult *= parseFloat(splitStr[i])
}
return persistence(parseFloat(mult));
}
The output for any single digit number will be 0 which is correct however for any number that is multiple digits, the persistence always logs as 1 and I can't seem to figure out why, any help would be greatly appreciated.

The posted code has quite a few problems.
for (var i = 0; i <= splitStr; i++) {
But splitStr is an array, not a number; i <= splitStr doesn't make sense. It should check against splitStr.length instead of splitStr.
Another problem is that it should use i <, not i <=, else the final splitStr[i] will be undefined.
Another problem is that the count variable is global, so more than one call of persistence will result in inaccurate results. There's no need for a count variable at all. To fix it:
function persistence(num) {
if (num.toString().length === 1) {
return 0;
}
var mult = 1;
var splitStr = num.toString().split("");
for (var i = 0; i < splitStr.length; i++) {
mult *= parseFloat(splitStr[i])
}
return 1 + persistence(parseFloat(mult));
}
console.log(
persistence(999),
persistence(39),
persistence(4)
);
Or, one could avoid the for loop entirely, and use more appropriate array methods:
function persistence(num) {
const str = num.toString();
if (str.length === 1) {
return 0;
}
const nextNum = str.split('').reduce((a, b) => a * b, 1);
return 1 + persistence(nextNum);
}
console.log(
persistence(999),
persistence(39),
persistence(4)
);

or we can use while loop with reduce array method
const persistence=(num)=>{
let splitNumArr=num.toString().split('')
let newList
let count=0
while(splitNumArr.length>1){
newList=splitNumArr.reduce((acc,curr)=>{
return acc*=curr
})
splitNumArr=newList.toString().split('')
count++
}
return count
}
console.log(persistence(39))===3
console.log(persistence(999))===4
console.log(persistence(9))===0

Related

How do I push arrays to array inside a recursive javascript function so that I can use it when it's done?

First question here (I think). Please let me know if additional information is needed in order for you guys to help me out.
So I'm trying to implement an algorithm in javascript that uses a recursive funtion.
The function is copied from Implementing Heap Algorithm of Permutation in JavaScript and looks like this:
let swap = function(array, index1, index2) {
let temp = array[index1]
array[index1] = array[index2]
array[index2] = temp
return array
}
let permutationHeap = (array, result, n) => {
n = n || array.length // set n default to array.length
if (n === 1) {
result(array)
} else {
for (let i = 1; i <= n; i++) {
permutationHeap(array, result, n - 1)
if (n % 2) {
swap(array, 0, n - 1) // when length is odd so n % 2 is 1, select the first number, then the second number, then the third number. . . to be swapped with the last number
} else {
swap(array, i - 1, n - 1) // when length is even so n % 2 is 0, always select the first number with the last number
}
}
}
}
let output = function(input) {
console.log(output)
}
permutationHeap([1,2,3,4,5], output)
The console.log in the output function (callback?) gives me the right output. If I move that console.log below the if statement in permutationHeap-function, I get the right output as well (console.log(array), in that case though).
What I want to do is to store every output as an array, inside an array that I can use later on down the road. I'm guessing that I'm struggling with Javascript 101 here. Dealing with asynchronus thinking. But can't for the life of me figure out how to get that array of arrays!
If I declare an empty array outside the permutationHeap-function and
.push(array) it only stores [1,2,3,4,5]. Same deal if I do the same
thing inside the output-function.
I've also tried passing in an empty array to the
permutationHeap-function and push that way. Still no luck.
Anyone who's willing to shine some light over a probably super nooby question? :) Much appriciated!
I actually broke the algorithm in my previous answer, because by duplicating the array ever iteration I broke the part of the algorithm that relies on the array changing as it goes.
I've made an answer that uses the code that you had originally more effectively:
var swap = function(array, index1, index2) {
var temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
return array;
};
var permutationHeap = function(array, result, n) {
n = n || array.length; // set n default to array.length
if (n === 1) {
result(array);
} else {
for (var i = 1; i <= n; i++) {
permutationHeap(array, result, n - 1);
if (n % 2) {
swap(array, 0, n - 1); // when length is odd so n % 2 is 1, select the first number, then the second number, then the third number. . . to be swapped with the last number
} else {
swap(array, i - 1, n - 1); // when length is even so n % 2 is 0, always select the first number with the last number
}
}
}
};
function getPermutations(array) {
var results = [];
var output = function(res) {
results.push(res.slice(0));
}
permutationHeap(array, output);
return results;
}
var permutations = getPermutations([1,2,3]);
console.log(permutations);
I think I've managed to fix your code by using generators and yield. I can't exactly explain it though...
var swap = function(array, index1, index2) {
let temp = array[index1]
array[index1] = array[index2]
array[index2] = temp
return array
}
var permutationHeap = function*(array, result, n) {
n = n || array.length // set n default to array.length
if (n === 1) {
yield (array.slice(0))
} else {
for (let i = 1; i <= n; i++) {
yield* permutationHeap(array, result, n - 1)
if (n % 2) {
swap(array, 0, n - 1) // when length is odd so n % 2 is 1, select the first number, then the second number, then the third number. . . to be swapped with the last number
} else {
swap(array, i - 1, n - 1) // when length is even so n % 2 is 0, always select the first number with the last number
}
}
}
}
var x = permutationHeap([1,2,3,4,5])
var results = Array.from(x);
console.log(results);

Overlapping loops inside functions

In code wars, i am training on a project of multiplicative inverse. The goal is, for example -- if "39" is the input the output should be "4" [39 = 3*9 ==> 27 = 2*7 => 14 = 1*4 ==> (4)].
i wrote some code which just multiplies the input for only first level(39 ==> 27).
My code until now,
function persistence(num) {
var digits = new Array();
digits = num.toString().split("").map(Number);
var res = 1;
for (i = 0; i < digits.length; i++) { res = res * digits[i]; }
return res;
console.log(persistence(digits));
}
i am just learning javascript and i am stuck here. I need to loop this process till i get a single digit number.
CAN SOMEONE PLEASE HELP ME?
Sorry if my question is not clear...
Array methods are your friend. In this case, use map and reduce:
function multiplyDigits(num) {
if (num < 10) return num;
console.log(num);
const multiplied = String(num)
.split('')
.map(Number)
.reduce((a, n) => a * n, 1);
return multiplyDigits(multiplied);
}
console.log(multiplyDigits(39));
Aside from formatting this is very similar to your code. I commented the only two changes:
function persistence(num) {
var digits = new Array();
digits = num.toString().split("").map(Number);
// 1) Added this if statement to return the result
// immediately if there is only one digit.
if ( digits.length === 1 )
return digits[0];
var res = 1;
for (i = 0; i < digits.length; i++)
res = res * digits[i];
// 2) Changed from `return res;` so that the function you wrote
// is called again on the result (if there were more than one digit).
return persistence(res);
}
console.log(persistence('39'));
For a bit shorter of a solution you could use:
function persistence(num) {
return num < 10
? num
: persistence( (num+'').split('').reduce( (a,b) => a*b ) );
}
console.log(persistence('39'));

Javascript Loop Performance: Counting occurrences of a number in a finite series

What is the most efficient way to write a javascript loop to calculate the number of occurrences of 7's (as an example number) that will be encountered in counting from 1 to 100?
Example:
function numberOccurences(targetNumber, minNumber, maxNumber) {
var count = 0;
for (i = minNumber; i < maxNumber; i++) {
count = count + (i.toString().split(targetNumber).length - 1);
}
return count;
}
var result = numberOccurences(7,1,100);
This will do it without looking at the actual numbers. Sorry, no loop, but you did ask for effeciency. If you really want to use a loop, make the recursion an iteration.
function digitOccurences(digit, min, max, base) {
if (typeof base != "number") base = 10;
return digitOccurencesPlus(digit, max, base, 1, 0) - digitOccurencesPlus(digit, min, base, 1, 0);
function digitOccurencesPlus(digit, N, base, pow, rest) {
if (N == 0) return 0;
var lastDigit = N%base,
prevDigits = (N-lastDigit)/base;
var occsInLastDigit = pow*(prevDigits+(lastDigit>digit));
var occsOfLastInRest = rest * (lastDigit==digit);
// console.log(prevDigits+" "+lastDigit, rest, occsInLastDigit, occsOfLastInRest);
return occsInLastDigit + occsOfLastInRest + digitOccurencesPlus(digit, prevDigits, base, pow*base, pow*lastDigit+rest);
}
}
This is an interesting problem, and already has similar answers for other languages. Maybe you could try to make this one in javascript: Count the number of Ks between 0 and N
That solution is for occurences from 0 to n, but you could easily use it to calculate from a to b this way:
occurences(a,b)= occurences(0,b)-occurences(0,a)
This is much faster (x6) than my original function...JSPERF
function numberOccurences2(targetNumber, minNumber, maxNumber) {
var strMe = "";
for (i = minNumber; i < maxNumber; i++) {
strMe = strMe.concat(i);
}
var re = new RegExp(targetNumber,"g");
var num1 = strMe.length;
var num2 = strMe.replace(re, "").length;
num2 = num1- num2;
return (num2);
}
There has to be a faster way still...

Javascript: Example of recursive function using for loops and substring - can't figure out where I'm going wrong

I'm currently working on coderbyte's medium challenge entitled "Permutation Step."
The goal is to take user input, num, and to return the next number greater than num using the same digits So, for example, if user input is 123, then the number 132 should be returned. If user input is 12453, then 12534 should be returned...
Anywho, I have a correct model answer created by someone (probably a genius, cuz this stuff is pretty hard) and I'm trying to figure out how the answer works, line for line by having an example play out (I'm keeping it simple and playing out the function with user input 123).
The answer has 2 functions, but the 1st function used is what I'm currently trying to work out...
The relevant code is:
var PermutationStep1 = function(num) {
var num1 = num.toString();
var ar = [];
//return num1;
if (num1.length < 2) {
return num;
} else {
for(var i = 0; i < num1.length; i++) {
var num2 = num1[i];
var num3 = num1.substr(0,i) + num1.substr(i+1, num1.length -1);
var numAr = PermutationStep1(num3);
for(var j = 0; j < numAr.length; j++) {
ar.push(numAr[j] + num2);
}
}
ar.sort(function(a,b) {return a-b});
return ar;
}
}
Again, I'm trying to work thru this function with the inputted num as 123 (num = 123).
I'm pretty sure that this function should output an array with multiple elements, because the 2nd function merely compares those array elements with the original user input (in our case, 123), and returns the next greatest value.
So in our case, we should probably get an array, named 'ar', returned with a host of 3 digit values. But for some reason, I'm getting an array of 2 digit values. I can't seem to isolate my mistake and where I'm going wrong. Any help with where, specifically, I'm going wrong (whether it be the recursion, the use of substring-method, or the concating of strings together, whatever my problem may be) would be appreciated...
Here's some of my work so far:
PS1(123) / num1 = 123
i = 0;
num2 = (num1[i]) = '1';
num3 = (num1.substr(0, 0) + num1.substr(1, 2)) = ('0' + '23') = '23'
PS1(23)
i = 0;
num2 = '2';
num3 = '3'
PS1(3) -> numAr = 3 (since num1 is less than 2 digits, which is the recursion base case?)
(So take 3 into the 2nd for loop)...
ar.push(numAr[j] + num2) = ar.push('3' + '1') = 31
ar = [31] at this point
And then I go through the initial for-loop a couple more times, where i = 1 and then i = 2, and I eventually get....
ar = [31, 32, 33]...
But I'm thinking I should have something like ar = [131, 132, 133]? I'm not sure where I'm going wrong so please help. Because the answer is correctly spit out by this function, the correct answer being 132.
Note: if you need the 2nd part of the model answer (i.e. the 2nd function), here it is:
var arr = [];
function PermutationStep(num1) {
arr.push(PermutationStep1(num1));
var arrStr = arr.toString();
var arrStrSpl = arrStr.split(",");
//return arrStrSpl;
for(var p = 0; p < arrStrSpl.length; p++) {
if(arrStrSpl[p] > num1) {
return arrStrSpl[p];
}
}
return -1;
}
I'm sorry the first function i posted was under a mathematical logical mistake and i was to overhasty
I thought about it again and now i have the following function which definitley works
function getNextNumber (num)
{
var numberStr=num.toString (), l=numberStr.length, i;
var digits=new Array (), lighterDigits, digitAtWeight;
var weight,lightWeight, lighterDigits_l, value=0;
for (i=l-1;i>-1;i--)
digits.push (parseInt(numberStr.charAt(i)));
lighterDigits=new Array ();
lighterDigits.push (digits[0]);
for (weight=1;weight<l;weight++)
{
digitAtWeight=digits[weight];
lighterDigits_l=lighterDigits.length;
for (lightWeight=0;lightWeight<lighterDigits_l;lightWeight++)
{
if (digitAtWeight<lighterDigits[lightWeight])
{
lighterDigits.unshift (lighterDigits.splice (lightWeight,1,digitAtWeight)[0]);
lighterDigits.reverse ();
digits=lighterDigits.concat (digits.slice (weight+1,l));
for (weight=0;weight>l;weight++)
value+=Math.pow (10,weight)*digits[weight];
return value;
}
}
lighterDigits.push (digitAtWeight);
}
return NaN;
}
okay here is my solution i found it in 20 minutes ;)
//---- num should be a Number Object and not a String
function getNextNumber (num)
{
var numberStr=num.toString (), l=numberStr.length, i;
var digits=new Array (), digitA, digitB;
var weight,lightWeight;
var valueDifference,biggerValue;
for (i=l-1;i>-1;i--)
digits.push (parseInt(numberStr.charAt(i))); // 345 becomes a0=5 a1=4 a2=3 and we can say that num= a0*10^0+ a1*10^1+ a2*10^2, so the index becomes the decimal weight
for (weight=1;weight<l;weight++)
{
digitA=digits[weight];
biggerValue=new Array ();
for (lightWeight=weight-1;lightWeight>-1;lightWeight--)
{
digitB=digits[lightWeight];
if (digitB==digitA) continue;
valueDifference=(digitA-digitB)*(-Math.pow(10,weight)+Math.pow (10,lightWeight));
if (valueDifference>0) biggerValue.push(valueDifference);
}
if (biggerValue.length>0)
{
biggerValue.sort();
return (biggerValue[0]+num);
}
}
}
this is the solution I figured out for the problem without using a recursive function. It's passed all the tests on coderbyte. I am still new to this so using recursion is not the first thing I look for. hope this can help anyone else looking for a solution.
function PermutationStep(num) {
var numArr = (num + '').split('').sort().reverse();
var numJoin = numArr.join('');
for (var i = (num + 1); i <= parseInt(numJoin); i++){
var aaa = (i + '').split('').sort().reverse();
if (aaa.join('') == numJoin){
return i;
}
}
return -1;
}

Validation for numeric data Javascript

I'm writing a program in Javascript that separates even and odd numbers, puts them into an array, adds the sum of numbers, and finds the average.
I'm having an issue not allowing zeros not to count. Because its adding to the array, and when the user types in 6+6, sum is 12, average is calculating to 4 because of the extra 0 in the array.
Is there anyway to not allow the zeros to count? Here is what I have so far..
var evenarray = [];
var oddarray = [];
var avgEven = 0;
var avgOdd = 0;
var isValid;
function numberFunction(){
do
{
var numbers = prompt("Please enter numbers. Enter empty string to exit.");
if(numbers % 2 == 0)
{
evenarray.push(numbers);
var sumEven = 0;
for (var i=0; i < evenarray.length; i++)
{
sumEven = sumEven + Number(evenarray[i]);
}
var avgEven = sumEven/evenarray.length;
//alert("even");
}
if(numbers % 2 !== 0)
{
oddarray.push(numbers);
var sumOdd = 0;
for (var i=0; i < oddarray.length; i++)
{
sumOdd = sumOdd + Number(oddarray[i]);
}
var avgOdd = sumOdd/oddarray.length;
//alert("odd");
}
//if(isNaN(numbers)){
//alert("Only numeric data only");
//}
}
while(numbers !== "");
Just do nothing when the number is actually 0:
if (numbers == 0)
{
}
else if(numbers % 2 == 0)
{
evenarray.push(numbers);
var sumEven = 0;
for (var i=0; i < evenarray.length; i++)
{
sumEven = sumEven + Number(evenarray[i]);
}
var avgEven = sumEven/evenarray.length;
}
else // only odds remain
{
oddarray.push(numbers);
var sumOdd = 0;
for (var i=0; i < oddarray.length; i++)
{
sumOdd = sumOdd + Number(oddarray[i]);
}
var avgOdd = sumOdd/oddarray.length;
}
You can do :
if(numbers % 2 == 0 && numbers !=0) ...
if(numbers % 2 != 0 && numbers !=0) ...
so that you don't do anything when numbers == 0;
It's a little strange to call your variable numbers instead of number.
your function should be,
function numberFunction(){
do
{
var numbers = prompt("Please enter numbers. Enter empty string to exit.");
if(numbers !=0 && !isNaN(numbers))
(numbers %2 == 0)? (evenarray.push(parseInt(numbers))) : (oddarray.push(parseInt(numbers)));
}while(numbers !== "");
for(var i = 0; i < evenarray.length; i++)
sumEven += evenarray[i];
for(var i = 0; i < oddarray.length; i++)
sumOdd += oddarray[i];
avgEven = sumEven / evenarray.length;
avgOdd = sumOdd / oddarray.length;
document.getElementById("even").innerHTML = evenarray.toString();
document.getElementById("sumEvenTotal").innerHTML = sumEven.toString(); //displays sum of even numbers.
document.getElementById("averageOdd").innerHTML = avgOdd; //displays average of odd numbers.
document.getElementById("averageEven").innerHTML = avgEven; //diplays average of even numbers.
document.getElementById("odd").innerHTML = oddarray.toString(); //displays all odd numbers that were entered.
document.getElementById("sumOddTotal").innerHTML = sumOdd.toString();
}
As you already have other answers with solutions to your particular issue, I would suggest a different approach. Think of the data you're manipulating: an array. Try to solve the issue only with data, no user input, no DOM manipulation; just data. This helps to separate concerns, and make your code easier to understand.
Since we're working with arrays, we can make use of some of the built-in JavaScript methods that are present in modern browsers, such as filter and reduce. These methods are in a way, alternatives to for loops, with some pre-defined behavior, and a callback function.
Now, let's think of the steps involved in solving your problem.
Get numbers from the user. We can represent this data as an array, as you were already doing.
We want all odd numbers, their sum and average.
We want all even numbers, their sum and average.
We display the data to the user.
In this solution I'm assuming you already have an array with the data, and will be focusing on points 2 and 3. Remember, think of data, user interaction shouldn't be mixed with your data logic. Instead of asking the user for a number on each loop, you could ask the user for a list of numbers directly; you avoid multiple prompts this way, and it lets you separate data and interaction nicely. Ideally you'd validate all user input to match your requirements.
// Helpers to work with numbers
var odd = function(x) {
return x % 2 === 0;
};
var even = function(x) {
return x % 2 !== 0;
};
var add = function(x, y) {
return x + y;
};
function solve(ns) {
// Solve the problem
// with odd or even numbers
var result = function(fn) {
var xs = ns.filter(fn); // odd or even
var sum = xs.reduce(add);
return {
numbers: xs,
sum: sum,
average: sum / xs.length
};
};
// Return an object
// with odd and even results
return {
odd: result(odd),
even: result(even)
};
}
var numbers = [1,2,3,4]; // from user input
var result = solve(numbers);
console.log(result.odd);
//^ {numbers: [2,4], sum: 6, average: 3}
console.log(result.even);
//^ {numbers: [1,2], sum: 4, average: 2}

Categories

Resources