Questions
Two recursive calls inside one recursive step: how bad is it?
In the code below I used some recursion, there are situations like
const somefunc = (x) => {
//here goes some base case
..........
//here goes some recursive step
..... return somefunc(x + 2) + somefunc (x + 4) // totally random generic example
}
I know that sometimes it’s really bad, like if we try to implement function like this, for fibonacci numbers:
const terribleFibs = (n) => {
if (n === 0 || n === 1) {
return 1;
}
else {
return terribleFibs(n - 1) + terribleFibs(n - 2);
}
}
console.log(terribleFibs(75))
It will be ridiculously slow.
However, in the code below, I used something like that, because in my case recursion won’t be really deep.
I want to know: can I use recursive calls like that sometimes, or it’s better to rewrite it with iteration? It will be more code with iteration tho.
Object lookups
I could use here only one object for lookup theoretically, but I decided to split it a little. It’s at the top of code. I would like to know, is there any difference: store all stuff in single object or split it like I did. Just for readability, I guess, my option is better.
AAnd I saw the hint page, they are using arrays of constants instead of objects, maybe I should do it too?
const singleDigitsToWord = {
0 : 'zero',
1 : 'one',
2 : 'two',
3 : 'three',
4 : 'four',
5 : 'five',
6 : 'six',
7 : 'seven',
8 : 'eight',
9 : 'nine'
}
const doubleDigitsToWord = {
10: 'ten',
11: 'eleven',
12: 'twelve',
'1xEnding' : 'teen',
3: 'thir',
4: 'four',
5: 'fif',
6: 'six',
7: 'seven',
8: 'eigh',
9: 'nine',
'2plusEnding': 'ty',
20: 'twen',
40: 'for'
}
//will be done
const threeAndfourDigitsToWord = {
3 : 'hundred',
4 : 'thousand',
'and' : 'and'
}
const lettersForNumber = (num) => {
const len = num.toString().length
//single digit number 0...9
if (len === 1) {
return singleDigitsToWord[num].length
}
//double digit number 10...99
if (len === 2) {
// 10, 11, 12
if (num >= 10 && num <= 12) {
return doubleDigitsToWord[num].length;
}
// 13...19
else if (num > 12 && num < 20) {
return (doubleDigitsToWord[num.toString()[1]]
+ doubleDigitsToWord['1xEnding']).length;
}
// 20, 30, 40... 90
else if (num % 10 === 0) {
//20, 40
if (num === 20 || num === 40) {
return (doubleDigitsToWord[num]
+ doubleDigitsToWord['2plusEnding']).length;
}
//rest of x0
else {
return (doubleDigitsToWord[num.toString()[0]]
+ doubleDigitsToWord['2plusEnding']).length;
}
}
// all xx except the above ones:
// 21...29, 31...39 etc.
//21 >>> twenty one; 45 >>> forty five
//can use recursive calls here
else {
let firstDigit = Number(Math.floor(num / 10) + '0');
let secondDigit = Number(num.toString()[1]);
return lettersForNumber(firstDigit) + lettersForNumber(secondDigit);
}
}
//three digit number 100...999
if (len === 3) {
const firstDigit = Number(num.toString()[0]);
//100, 200, 300... 900
if (num % 100 === 0) {
return lettersForNumber(firstDigit)
+ threeAndfourDigitsToWord[3].length;
}
//101...109, 401...409, 501...509 etc.
else if (num % 100 < 10) {
const lastDigit = Number(num.toString()[2]);
return lettersForNumber(firstDigit)
+ threeAndfourDigitsToWord[3].length
+ threeAndfourDigitsToWord['and'].length
+ lettersForNumber(lastDigit);
}
// all xxx except the above ones
else {
const lastTwoDigits = Number(num.toString().substring(1));
return lettersForNumber(firstDigit)
+ threeAndfourDigitsToWord[3].length
+ threeAndfourDigitsToWord['and'].length
+ lettersForNumber(lastTwoDigits);
}
}
//TODO write code for all 4-digits after passing the tests
//now it's just for num === 1000
if (len === 4) {
return 'onethousand'.length
}
}
function numberLetterCounts(limit) {
let counter = 0;
for (let i = 1; i <= limit; i++) {
counter += lettersForNumber(i);
}
return counter;
}
numberLetterCounts(5);