I'm supposed to get the size of longest incremental numbers of all given numbers. I find the size in arithemetic way.. But the question is to find it in recursive fashion.. I did it but only the last value is showing up.. I dont know what's wrong...
function findmax(num, u, max) {
let a = parseInt(num);
let last = parseInt(a % 10);
let slast = parseInt((a / 10) % 10);
if (max > 0) {
console.log(u + " " + slast + " " + last);
if (slast <= last) {
// console.log("Great");
return findmax(parseInt(a / 10), u + 1, max - 1);
} else {
// Here is the problem.. If I do return findMax(parseInt(a / 10), 1, max - 1) only last value of u is shown is shown.. Now v is always undefined, idk why
v = findmax(parseInt(a / 10), 1, max - 1);
// console.log("a");
if (u >= v) return u;
else return v;
}
}
}
let a = [8897727547, 9876543210, 7778124589, 7778121982, 6723198999, 8000008999, 9800236046];
a.forEach(v => {
console.log(v + " : " + findmax(v, 1, 9));
});
Outputs for the aboeve numbers are : 3, 1, 6, 4, 4, 9, 5
This solution creates findMax by converting the number to a string of digits and then passing it to streak. streak is a recursive function that takes an array of digits, and then defaults three parameters: the previous digit, the count of the current streak and the length of the longest streak.
When we run out of digits, we return the current maximum. When our current digit is at least as large as the previous one, we recur, increasing our current streak by one, and updating our maximum if the new current streak is higher. If not, we recur, starting over with the current digit as previous, a current streak of one and the existing maximum.
const streak = ([d, ...ds], prev = -1, curr = 0, max = 0) =>
d == undefined
? max
: d >= prev
? streak (ds, d, curr + 1, Math.max (max, curr + 1))
: streak (ds, d, 1, max)
const findMax = (n) =>
streak (String (n))
let a = [8897727547, 9876543210, 7778124589, 7778121982, 6723198999, 8000008999, 9800236046];
a .forEach (n => console .log (`${n}: ${findMax(n)}`))
Update
There was a comment about not wanting to use arrays for this. The array was just a convenience. It allowed us to destructure the first and remaining values of our string into useful variables. But there is nothing special about it, and our algorithm works the same with only minor tweaks, if we use a string of digits instead:
const streak = (digits, prev = -1, curr = 0, max = 0) =>
digits .length == 0
? max
: digits [0] >= prev
? streak (digits .slice (1), digits [0], curr + 1, Math.max (max, curr + 1))
: streak (digits .slice (1), digits [0], 1, max)
Update 2
The wonderful answer from #Thankyou offers a challenge to do this without anything more than the basic mathematical operators and function calls. It's not hard to write a version of this same algorithm that works this way, but since we were working with digits, it was nothing that came to mind when I first read the question. Here's a variant:
const streak = (n, prev, curr, max) =>
n < 1
? max
: n % 10 <= prev
? streak (n / 10 >> 0, n % 10, curr + 1, curr + 1 > max ? curr + 1 : max)
: streak (n / 10 >> 0, n % 10, 1, max)
const findMax = (n) =>
streak (n, -1, 0, 1)
let a = [8897727547, 9876543210, 7778124589, 7778121982, 6723198999, 8000008999, 9800236046];
a .forEach (n => console .log (`${n}: ${findMax(n)}`))
Here we break a number n into it's final digit n % 10 and the rest of the digits as a single number n / 10 >> 0. If the >> is not familiar, it's a
(truncating) shift operator for integers. n >> 0 for positive numbers is equivalent to Math .floor (n). So if n is 12345, then n % 10 is 5 and n / 10 >> 0 is 1234.
There are two chief differences from the previous version. First, I've moved the initialization of the parameters from default parameters in the internal streak to arguments supplied by the wrapper function findMax. This has little to do with the main changes here, but somehow this time just felt cleaner. Second, because it's mathematically easier to break 12345 into 1234 and 5 than to break it into 1 and 2345, we work from the last digit to the first in this version, which means that we change from a >= test to a <= one, and also that we have to start max off at 1.
The new function should feel similar to the previous two versions, but in this case it does all this based on mathematical rather than String/Array versions.
Thank you, #Thankyou for noting the possibilities here!
"I was supposed to do this without any arrays..."
Programming with restrictions is fun and forces us to think about solving problems in new ways. In this answer, we will only use primitive features of JavaScript -
Numbers, 1, 2, -Infinity, etc
Comparison operators, ==, <, >=, ||, etc
Basic arithmetic, +, /, %, >>, etc
Ternary expressions, cond ? ifTrue : otherwise
Functions, (arg1, arg2, ...) => result
String and compound data types like Array or Object will not be used.
implementation
Let's start with findMax -
const findMax = n =>
streak // <- calculate streak of
( pairwise(digits(n)) // <- pairwise digits
, tuple(1, -Infinity) // <- starting with streak of 1, max streak of -Infinity
)
As we wish for functions to exist, we implement them along the way. Let's see tuple -
const tuple = (a, b) => // <- tuple of (a,b)
k => k(a, b) // <- is a continuation of (a,b)
const car = t => // <- car of a tuple
t((a, _) => a) // <- is the first element, a
const cdr = t => // <- cdr of a tuple
t((_, b) => b) // <- is the second element, b
car(tuple(7,9)) // -> 7
cdr(tuple(7,9)) // -> 9
Now let's write digits -
const nil =
Symbol("nil") // <- empty list sentinel
const digits = n =>
n < 10 // <- when n is a single digit
? tuple(n, nil) // <- tuple of single digit and nil
: tuple(n % 10, digits(n / 10 >> 0)) // <- tuple of first digit and rest of digits
The digits will be compared pair-wise. Given 8897727547 we want to compare pairs of digits and respond to pairs where the digits are increasing -
(8,8) (8,9) (9,7) (7,7) (7,2) (2,7) (7,5) (5,4) (4,7)
= < > = > < > > <
We can implement this as pairwise -
const pairwise = t =>
zip(t, skip(t, 1))
const skip = (t, n) =>
t == nil || n == 0
? t
: cdr(t)
const zip = (t0, t1) =>
t0 == nil || t1 == nil
? nil
: tuple
( tuple(car(t0), car(t1))
, zip(cdr(t0), cdr(t1))
)
Finally we implement streak. As we already saw, it takes a pairwise list of digits and an initial result tuple of 1, -Infinity -
const streak = (t, r) =>
t == nil
? cdr(r)
: call
( n =>
streak
( cdr(t)
, tuple(n, max(cdr(r), n))
)
, caar(t) >= cadr(t)
? car(r) + 1
: 1
)
Which has simple dependencies caar, cadr, max, and call -
const caar = t =>
car(car(t))
const cadr = t =>
cdr(car(t))
const max = (a, b) =>
a > b ? a : b
const call = (f, x) =>
f(x)
Now we run findMax with your input examples -
findMax(8897727547) // -> 3
findMax(9876543210) // -> 1
findMax(7778124589) // -> 6
findMax(7778121982) // -> 4
findMax(6723198999) // -> 4
findMax(8000008999) // -> 9
findMax(9800236046) // -> 5
free data types and functions
As a result of this implementation, we ended up with some new data types and generic functions which are useful in all sorts of ways -
tuple a way to group primitive data together, and even construct lists
car, cdr, caar, and cadr give us a way to access elements within our tuples
digits constructs a sequence of tuples, representing single digits of an input number
zip, skip, and pairwise give us a generic way to manipulate sequences of tuples
max for getting the maximum of two values, and call for calling a function with an argument
demo
Expand the snippet below to verify the results in your own browser -
const nil =
Symbol("nil")
const tuple = (a, b) =>
k => k(a, b)
const car = t =>
t((a, _) => a)
const cdr = t =>
t((_, b) => b)
const caar = t =>
car(car(t))
const cadr = t =>
cdr(car(t))
const digits = n =>
n < 10
? tuple(n, nil)
: tuple(n % 10, digits(n/10 >> 0)))
const skip = (t, n) =>
t == nil || n == 0
? t
: cdr(t)
const zip = (t0, t1) =>
t0 == nil || t1 == nil
? nil
: tuple
( tuple(car(t0), car(t1))
, zip(cdr(t0), cdr(t1))
)
const pairwise = t =>
zip(t, skip(t, 1))
const max = (a, b) =>
a > b ? a : b
const call = (f, x) =>
f(x)
const streak = (t, r) =>
t == nil
? cdr(r)
: call
( n =>
streak
( cdr(t)
, tuple(n, max(cdr(r), n))
)
, caar(t) >= cadr(t)
? car(r) + 1
: 1
)
const findMax = n =>
streak
( pairwise(digits(n))
, tuple(1, -Infinity)
)
console.log(8897727547, findMax(8897727547)) // 3
console.log(9876543210, findMax(9876543210)) // 1
console.log(7778124589, findMax(7778124589)) // 6
console.log(7778121982, findMax(7778121982)) // 4
console.log(6723198999, findMax(6723198999)) // 4
console.log(8000008999, findMax(8000008999)) // 9
console.log(9800236046, findMax(9800236046)) // 5
additional reading
If you're curious where names like car, cdr, caar, and cadr come from, see Racket: Pairs and Lists.
alternatives
This of course is not the only way to solve the problem without using arrays. Scott's wonderful and straightforward answer could easily be changed to use lesser features and still arrive at the correct result. As is often the case with programming, there are countless ways to solve any specific problem, each involving techniques that make distinct trade offs. Anyhow, I hope this answer was able to teach you something fun about programming with restrictions.
Related
This question has been bugging me ever since I got it in a Interview and couldn't figure it out and I've not been able to find a solution anywhere, especially in Javascript:
Given 4 Digits, count how many valid Times can be displayed ib a Digital Clock (in 24hr format) using those digits. Earliest Time is 00:00 and latest is 23:59.
Write a Function:
function solution(A,B,C,D);
That, given 4 intergers A,B,C,D, returns the number of valid times that can be displayed on a digital clock
Start by writing a validating function that tells you whether 4 digits make up a legit time:
function validate(A,B,C,D){
let hours = +("" + A + B),
mins = +("" + C + D);
return hours <= 23
&& hours >= 0
&& mins >= 0
&& mins < 60
}
Then given 4 digits, before you can use the above, you need to generate random orders of those 4 digits. So take any permutation function:
const permutations = arr => {
if (arr.length <= 2) return arr.length === 2 ? [arr, [arr[1], arr[0]]] : arr;
return arr.reduce(
(acc, item, i) =>
acc.concat(
permutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map(val => [
item,
...val,
])
),
[]
);
};
This will take an array of numbers like permutations([1,2,4,5]). The issue is this function will generate 24 element array even if you give it [1,1,1,1]. So you need to filter the unique ones:
const unique = arr => {
return arr.reduce((ac,d) => {
if(!ac.some(perm => d.join("") === perm.join(""))){
ac.push(d);
}
return ac;
},[])
}
Now you can write your solution combining above 3 functions:
function solution (A,B,C,D){
return unique(permutations([A,B,C,D])).reduce((ac,d) => ac += validate(...d),0)
}
For example:
solution(1,1,6,1) //2
I am absolutely sure it can written tidier and more concise, I might have forgotten something as well, so take everything with a pinch of salt
You're pretty new here. A reminder if you haven't done it, please take the tour, visit the help center and read up on asking good questions. After doing some research and searching for related topics on SO, try it yourself. If you're stuck, post a minimal, reproducible example of your attempt and note exactly where you're stuck.
I wouldn't answer without your demonstrated effort if there was not already a working answer posted.
Because there are only 24 permutations of the four digits, it might be easiest just to include them directly in the code. One solution would use a simple predicate (isValidTime) on four digits to see if they constitute a valid time, and then use a string representation of each possible permutation, convert them to four-digit strings, use the [... new Set (xs)] trick to find an array of the unique ones, collect the ones that match our validity predicate, and finally return the length of that array. It might look like this:
const isValidTime = (A, B, C, D, h = 10 * A + B, m = 10 * C + D) =>
0 <= h && h <= 23 && 0 <= m && m <= 59 // ? `${A}${B}${C}${D}` : false
const countValidTimes = (A, B, C, D, x = {A, B, C, D}) =>
[... new Set ([
"ABCD", "ABDC", "ACBD", "ACDB", "ADBC", "ADCB", "BACD", "BADC",
"BCAD", "BCDA", "BDAC", "BDCA", "CABD", "CADB", "CBAD", "CBDA",
"CDAB", "CDBA", "DABC", "DACB", "DBAC", "DBCA", "DCAB", "DCBA"
] .map (([...cs]) => cs .map (c => x [c]) .join (''))
)] .filter (s => isValidTime (... s .split ('') .map (d => Number(d)))) .length
console .log (countValidTimes (8, 6, 1, 5))
//=> 2 (18:56, 16:58)
console .log (countValidTimes (1, 2, 3, 4))
//=> 10 (12:34, 12:43, 13:24, 13:42, 14:23, 14:32, 21:34, 21:43, 23:14, 23:41)
console .log (countValidTimes (1, 4, 1, 4))
//=> 3 (14:14, 14:41, 11:44)
console .log (countValidTimes (1, 1, 1, 1))
//=> 1 (11:11)
console .log (countValidTimes (8, 6, 7, 5))
//=> 0
maybe not the best solution but i think it's ez to understand. I used backtracking to resolve this problem. Faced this question in an interview too :D.
public static void main(String[] args) {
//Driver
int result = solution(6, 2, 4, 7);
System.out.println(result);
}
public static int solution(int a, int b, int c, int d) {
int[] arr = {-1, -1, -1, -1};
Map<Integer, Integer> tracking = new HashMap<>();
tracking.put(a, 0);
tracking.put(b, 0);
tracking.put(c, 0);
tracking.put(d, 0);
int[] track = {a, b, c, d};
for (int i = 0; i < track.length; i++) {
tracking.put(track[i], tracking.get(track[i]) + 1);
}
Set<String> set = new HashSet<>();
possibleTime(a, b, c, d, arr, 0, tracking,set);
return set.size();
}
public static int[] possibleTime(int a, int b, int c, int d, int[] arr, int index, Map<Integer, Integer> tracking,Set<String> set) {
if (index == 4) {
set.add(Arrays.toString(arr));
return arr;
}
int[] pos = {a, b, c, d};
for (int i = 0; i < pos.length; i++) {
arr[index] = pos[i];
tracking.put(pos[i], tracking.get(pos[i]) - 1);
if (isValidTime(arr, tracking,set)) {
index++;
arr = possibleTime(a, b, c, d, arr, index, tracking,set);
index--;
}
tracking.put(pos[i], tracking.get(pos[i]) + 1);
arr[index] = -1;
}
return arr;
}
public static boolean isValidTime(int[] arr, Map<Integer, Integer> tracking,Set<String> set) {
//check existed
for (Integer in : tracking.keySet()) {
if (tracking.get(in) < 0) {
return false;
}
}
if(set.contains(Arrays.toString(arr)))
{
return false;
}
//validate hh
Map<Integer, Integer> hh = new HashMap<>();
hh.put(1, 9);
hh.put(2, 3);
if (arr[0] != -1) {
if (hh.containsKey(arr[0])) {
if (arr[1] != -1 && arr[1] > hh.get(arr[0])) {
return false;
}
} else {
return false;
}
}
//validate mmm
if (arr[2] != -1 && arr[2] > 5) {
return false;
}
if (arr[3] != -1 && arr[3] > 9) {
return false;
}
return true;
}
def solution(A, B, C, D):
times = 0
# check all possible hours
for hour_tens in range(2):
for hour_units in range(4):
if (hour_tens == 2 and hour_units > 3):
continue
# check all possible minutes
for min_tens in range(6):
for min_units in range(10):
if (str(hour_tens) + str(hour_units) + str(min_tens) + str(min_units)).count(str(A)) + (str(hour_tens) + str(hour_units) + str(min_tens) + str(min_units)).count(str(B)) + (str(hour_tens) + str(hour_units) + str(min_tens) + str(min_units)).count(str(C)) + (str(hour_tens) + str(hour_units) + str(min_tens) + str(min_units)).count(str(D)) == 4:
times += 1
return times
I've been thinking that I found solution, but tests fail (I have no access to tests).
The task:
We want to organize a chess tournament.
One day - one game.
And as entry point we have number of days - that we can afford.
Tournament rules:
We have number of teams: if number is even - we divide number of teams by 2 and that's the number of games - so they fight 1 vs 1 in pairs, who lost is excluded from the competition.
For example : 20 teams => 10 games => 10 teams left.
And if the even number of teams left - we make the same iteration: divide by 2 into pairs.
10 teams - 5 games - 5 teams left.
And now we have Odd number!
If we have odd number from start or get it in process - number of games is counted by another rule:
everyone needs to play with everyone. So the formula is = n * (n-1) / 2.
For example 5 teams = 10 games.
Or any odd number (to sum it all - if we have Odd number we always ignore first rule with pairs).
So:
We have number of days and need to find all possible team numbers for this days, it can be more than one value, for example : 3 days, number of teams that is correct is 3 and 4;
Entry point 3, function logs 3 and 4;
If that's impossible to find teams - return -1;
1 ≤ 𝑁 ≤ 1000000
Here is my solution - It ouputs correct values, but tests fail
Can someone help to find what I did wrong?
(Sorry for code mess, I had different thoughts every time)
function solve(input) {
let quadraticEquation = (number) => {
let a = 1;
let b = -1;
let c = -number*2
if(a == 0)
return 'false';
let res = {};
let D = b * b - 4 * a * c;
let tmp = [];
if(D < 0)
return 'false';
res['discriminant'] = D;
if(D == 0)
res["quadratic roots"] = (-b + Math.sqrt(D)) / (2 * a);
else if(D > 0){
tmp.push((-b + Math.sqrt(D)) / (2 * a));
tmp.push((-b - Math.sqrt(D)) / (2 * a));
res["quadratic roots"] = tmp;
}
return tmp;
}
let start = input;
let xValues = quadraticEquation(start).map(item => Math.abs(item)).filter(item => item % 2 !== 0);
let arrayToUse = []
for(i = 0; i <= xValues[xValues.length-1]; i++) {
if( i === 1) {
continue;
}
if (!(i%2)) {
continue
}
arrayToUse.push(i);
}
const answer = [];
arrayToUse.forEach(item => {
let startValue = item;
let fightsNumber = item * (item - 1) / 2;
let loop = 0;
if (fightsNumber === start) {
answer.push([startValue, loop])
} else {
do {
loop++;
fightsNumber += startValue;
if (fightsNumber === start) {
answer.push([item, loop] )
}
startValue *= 2;
} while (fightsNumber < start)
}
})
function getTeams (answer) {
const finalResult = [];
answer.forEach(item => {
if(item[1] === 0) {
finalResult.push(item[0]);
} else {
let initialValue = item[0];
for(i=0; i < item[1]; i++) {
initialValue += item[0];
item[0] *= 2;
}
finalResult.push(initialValue);
}
})
let initialValue = 2;
let fightCounter = 0;
for (i = 0; i <= start; i++) {
fightCounter += initialValue / 2
if (fightCounter === start) {
finalResult.push(initialValue);
}
initialValue *= 2;
}
return finalResult;
}
let finalString = ''
const arrayToLog = getTeams(answer).sort((a,b) => a - b);
if(arrayToLog.length !== 0) {
arrayToLog.forEach(item => {
finalString += item + '\n';
})
} else {
finalString += -1;
}
return finalString;
}
console.log(solve(325))
console.log(solve(3))
console.log(solve(15))
console.log(solve(21))
console.log(solve(10))
console.log(solve(1))
console.log(solve(5))
console.log(solve(9))
Note: for those who didn't see it, this is a followup to the earlier question Backward recurssion.
That's a lot of code for a fairly simple problem. I don't have the inclination or the time right now to go through it carefully.
So here is how I might solve this problem, using brute force to look for all the resulting number of days for any number of players up to days + 1, and then filtering out what we want from that list:
const range = (lo, hi) =>
[... Array (hi - lo + 1)] .map ((_, i) => i + lo)
const qwe = (n) =>
n % 2 == 1
? n * (n - 1) / 2
: n / 2 + qwe (n / 2)
const solve = (days) => {
const daysByPlayers = range (1, days + 1) .map (qwe)
const res = daysByPlayers .reduce ((a, d, i) => d == days ? [...a, i + 1] : a, [])
return res .length == 0 ? [-1] : res
}
const tests = [325, 3, 15, 21, 10, 1, 5, 9]
tests .forEach (n => console .log (`${n}: ${JSON.stringify(solve(n))}`))
.as-console-wrapper {max-height: 100% !important; top: 0}
This matches your output as far as I can tell. But I don't know what tests you say are failing, so perhaps there's something wrong with this as well.
There is extra complexity in here because of your desire for a -1 output in the missing cases. If an empty array would serve, then this is cleaner:
const solve = (days) =>
rng (1, days + 1) .map (qwe)
.reduce ((a, d, i) => d == days ? [...a, i + 1] : a, [])
And there are many variants. This one returns the maximum number of players that can be accommodated in some number of days up to the given value:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return days - daysByPlayers .reverse () .indexOf (max) + 1
}
And this one gives you all the possible number of players available to achieve that maximum:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return daysByPlayers .reduce ((a, d, i) => d == max ? [...a, i + 1] : a, [])
}
(For instance, solve (20) would return [10, 16], which are the possible number of players to handle in exactly 15 days, the largest value found no larger than 20.)
And finally, we could list all the numbers of players we could accommodate in up to a certain number of days:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return daysByPlayers .reduce ((a, d, i) => d <= max ? [...a, i + 1] : a, [])
}
For instance, solve (20) would return [1, 2, 3, 4, 5, 6, 8, 10, 12, 16], which are the only numbers of players that could be handled in no more than 20 days.
When I brought up the quadratic formula in the earlier question it was just to note that in certain instances, we can find an exact solution, solving for d in n * (n - 1) / 2) = d. That meant that if 8 * d + 1 is a perfect square, then we can choose n to be (1 + sqrt (8 * d + 1)) / 2 to find one solution. But that's not of any real help in the general case.
Currently trying to complete the persistent bugger kata in code wars.
I need to return the number of times the input has to be multiplied until it is reduced to a single number (full task instructions below).
Everything appears to work apart from the count. when I console log, the number of times it logs and runs is correct, but instead of incrementing such as 1,2,3 it will be something like 2,2,4.
So instead of returning 3, it returns 4.
I try to not ask for help with katas, but this time I am completely at a loss as to why the count is firstly skipping numbers and also not incrementing.
Task:
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.
For 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
My function:
function persistence(num) {
//console.log('here', num)
if(num < 10) return 0;
if(num === 25) return 2
let spl = num.toString().split('');
let result = 1;
let count = 1;
spl.forEach((s) => {
let int = parseInt(s)
result *= int;
//count++;
})
//console.log(result)
if(result > 9) {
persistence(result)
count++;
}
// console.log('count-->', count)
return count;
}
A sub issue is that the input 25 always returns a count 1 less than it should. My fix is poor I know, again any advice would be much appreciated.
Spoiler alert: this contains a solution. If you don't want that, stop before the end.
You don't really want to work with count, since as people point out, it's a local variable. You also don't work too hard to special case the result if it's a single digit. Let the recursion handle it.
Thus:
function persistence(num) {
//console.log('here', num)
if(num < 10) return 0;
//still here, must be 2 or more digits
let spl = num.toString().split('');
let result = 1;
spl.forEach((s) => {
let int = parseInt(s)
result *= int;
})
//console.log(result)
return 1 + persistence(result)
}
As you already have a complete solution posted here with fixes to your implementation, I will offer what I think is a simpler version. If we had a function to create the digit product for us, then persistence could be this simple recursion:
const persistence = (n) =>
n < 10 ? 0 : 1 + persistence (digProduct (n))
You already have code for the digit product, and while it's fine, a mathematical approach, rather than a string-base one, is somewhat cleaner. We could write it -- also recursively -- like
const digProduct = (n) =>
n < 10 ? n : (n % 10) * digProduct (Math .floor (n / 10))
or instead we might choose (n / 10) | 0 in place of the floor call. Either is reasonable.
Putting it together, we have:
const digProduct = (n) =>
n < 10 ? n : (n % 10) * digProduct ((n / 10) | 0)
const persistence = (n) =>
n < 10 ? 0 : 1 + persistence (digProduct (n))
const tests = [39, 999, 4, 25]
tests.forEach (n => console.log (`${n} --> ${persistence(n)}`))
I need to generate the custom sequence as below using javascript based on input. For ex: if i provide the input isAA1 then output should be AA2 and if provided input as AA9 then the output should be AB0. I can do with the if else by creating the token's but it looks like we need to keep so many if else condition's. Wanted to know the more efficient way to handle this.
AA0
AA1
AA2
AA3
AA4
AA5
AA6
AA7
AA8
AA9
AB0
AB1
AB2
AB3
AB4
AB5
AB6
AB7
AB8
AB9
AC0
AC1
.
.
.
ZZ9
Using reduceRight() you can iterate over the spread string in reverse, incrementing each character as necessary using the character's charCodeAt() value adjusted by its offset from 0, and cycling it based on its remainder (%) for the appropriate cycle length (10 for integers, 26 for letters). The accumulator tracks whether to sum on the next iteration, as well as the result string.
const incrementAlphaNumeric = (str) => {
let iter, min, len;
return [...str]
.reduceRight((a, c, i) => {
let code = c.charCodeAt();
if (code >= 48 && code <= 57) { // [0-9]
min = 48;
len = 10;
} else if ((code >= 65 && code <= 90)) { // [A-Z]
min = 65;
len = 26;
}
iter = code - min + a.sum;
a.res[i] = String.fromCharCode(iter % len + min);
a.sum = Math.floor(iter / len);
return a;
}, { res: [], sum: 1 })
.res
.join('');
}
console.log(incrementAlphaNumeric('AA0')); // AA1
console.log(incrementAlphaNumeric('AA9')); // AB0
console.log(incrementAlphaNumeric('AZ9')); // BA0
console.log(incrementAlphaNumeric('ZZ9')); // AA0
I had a little time on my hands, and this seemed like a fun challenge, so I went ahead and built a function that accepts any string of uppercase letters and/or numbers. I realize it might be a little bit overkill for the requirements of the question, but it does satisfy all of the requirements, and someone else stumbling across this question in the future might find this helpful.
It works by converting the right-most character to its respective character code, incrementing it by 1, and checking if the resulting character code is outside of the A-Z or 0-9 range. If it is outside of its range, we reset it to its "base value" (A or 0) and set a carry flag (this is very similar to how binary adder circuits work). If the carry flag is set, we recursively call the function using the next-to-last character as our new "right-most" character until we no longer need to carry. At which point, we simply return the new string.
increment('AA0') > 'AA1
increment('AA9') > 'AB0
increment('ZZ9') > 'AA0
increment('AZ9BE') > 'AZ9BF
const A = 65
const Z = 90
const ZERO = 48
const NINE = 57
const isDigit = (char) => char >= 48 && char <= 57
const incDigit = (char) => char + 1 > NINE ? ZERO : char + 1
const incLetter = (char) => char + 1 > Z ? A : char + 1
const codeToChar = (code) => String.fromCharCode(code)
const setChar = (index, char, str) => {
const charArr = [...str]
charArr.splice(index, 1, char)
return charArr.join('')
}
const increment = (str, place = str.length - 1) => {
if (place < 0) return str;
const char = str.charCodeAt(place)
const nextChar = isDigit(char) ? incDigit(char) : incLetter(char)
const carry = nextChar - char !== 1;
str = setChar(place, codeToChar(nextChar), str)
if (carry)
return increment(str, --place)
else return str
}
let demoStr = 'AA0'
setInterval(() => {
demoStr = increment(demoStr)
console.log(demoStr)
}, 25)
You could use this next() function, which increments the letters of the input string by converting them to base 26 numeric strings and back whenever an overflow is detected from incrementing the decimal portion of the input string:
const next = (() => {
const charCodeA = 'A'.charCodeAt(0);
const to = (replacer, string) => string.replace(/./g, replacer);
const padStart = (string, { length }, pad) => string.padStart(length, pad);
const truncate = (string, { length }) => string.slice(-length);
const letter = (base26String) => String.fromCharCode(
parseInt(base26String, 26) + charCodeA
);
const base26 = (letterString) => (
letterString.charCodeAt(0) - charCodeA
).toString(26);
const increment = (numbersString, radix) => (
parseInt(numbersString, radix) + 1
).toString(radix);
return (prev) => {
const [, prevL, prevD] = prev.match(/^([A-Z]+)([0-9]+)$/);
const nextD = padStart(increment(prevD, 10), prevD, '0');
const carry = nextD.length > prevD.length;
const nextL = carry
? padStart(to(letter, increment(to(base26, prevL), 26)), prevL, 'A')
: prevL;
return truncate(nextL, prevL) + truncate(nextD, prevD);
};
})();
console.log(next('AA0')); // AA1
console.log(next('AZ9')); // BA0
console.log(next('ZZ9')); // AA0
console.log(next('AAA00')); // AAA01
console.log(next('AZZ09')); // AZZ10
console.log(next('AZZ99')); // BAA00
console.log(next('ZZZ99')); // AAA00
References
String.prototype.charCodeAt()
String.prototype.replace()
String.prototype.padStart()
String.prototype.slice()
String.fromCharCode()
parseInt()
Number.prototype.toString()
String.prototype.match()
You could take two function for gettign a deciaml or the fancy value and check if the last digit is nine.
function increment(value) {
const
decimal = s => Array
.from(s, v => parseInt(v, 36) - 10)
.reduce((s, v) => s * 26 + v, 0),
fancy = n => Array
.from(n.toString(26), v => (parseInt(v, 26) + 10).toString(36))
.join('')
.toUpperCase(),
[left, right] = value.match(/\D+|\d+/g);
if (value === 'ZZ9') return 'AA0';
return right === '9'
? fancy(decimal(left) + 1) + '0'
: left + (+right + 1);
}
console.log(increment('AA0')); // AA1
console.log(increment('AZ9')); // BA0
console.log(increment('ZZ9')); // AA0
My experience with recursive calls is not deep or long, but I thought this problem would make for a good evaluation. Most of it appears to work, but the function returns undefined:
const target = 1234; // count the number of digits
function numDigits(num, count=0){
let div10 = count;
div10 += 1;
if(Math.floor(num / 10) < 1){
console.log(`Entered the end and div10 is ${div10}`);
return div10; // returning undefined
}
let curr = Math.floor(num / 10);
console.log(`Bottom of call and dividing ${curr}`);
numDigits(curr, div10);
}
numDigits(target);
You miss a return when calling recursively numDigits:
const target = 1234; // count the number of digits
function numDigits(num, count=0){
let div10 = count;
div10 += 1;
if(Math.floor(num / 10) < 1){
console.log(`Entered the end and div10 is ${div10}`);
return div10; // returning undefined
}
let curr = Math.floor(num / 10);
console.log(`Bottom of call and dividing ${curr}`);
return numDigits(curr, div10); // HERE
}
console.log(numDigits(target));
Recursion is a functional heritage and so using it with functional style yields the best results. There's no "missing return"; there's little need for return in functional style as it is a side effect, it does not produce a value, it cannot be called like a function, and it cannot be combined with other expressions. Other imperative style statements like the reassignment/mutation div10 += 1 are also a recipe for a migraine when used recursively.
Your program can be dramatically simplified –
const numDigits = n =>
n < 10
? 1
: 1 + numDigits (Math .floor (n / 10))
console .log
( numDigits (1) // 1
, numDigits (22) // 2
, numDigits (333) // 3
, numDigits (999999999) // 9
)
The count variable can still be used if you prefer. This would be a practical step toward making the program stack-safe –
const numDigits = (n, count = 1) =>
n < 10
? count
: numDigits
( Math .floor (n / 10)
, count + 1
)
console .log
( numDigits (1) // 1
, numDigits (22) // 2
, numDigits (333) // 3
, numDigits (999999999) // 9
)