Hello I'm trying to understand recursion in JavaScript.
So far I have:
function countVowels(string) {
let vowelCount = 0;
// if we're not at the end of the string,
// and if the character in the string is a vowel
if (string.length - 1 >= 0 && charAt(string.length -1) === "aeiouAEIOU") {
//increase vowel count every time we iterate
countVowels(vowelCount++);
}
return vowelCount;
}
First of all, this is giving me issues because charAt is not defined. How else can I say "the character at the current index" while iterating?
I can't use a for-loop - I have to use recursion.
Second of all, am I using recursion correctly here?
countVowels(vowelCount++);
I'm trying to increase the vowel count every time the function is called.
Thanks for your guidance.
If you're interested, here is a version that does not keep track of the index or count, which might illuminate more about how the recursion can be done.
function countVowels(string) {
if (!string.length) return 0;
return (
"aeiou".includes(string.charAt(0).toLowerCase()) +
countVowels(string.substr(1))
);
}
console.log(countVowels("")); // 0
console.log(countVowels("abcde")); // 2
console.log(countVowels("eee")); // 3
// Note that:
console.log('"hello".substr(1)', "hello".substr(1)) // ello
console.log('"hello".charAt(0)', "hello".charAt(0)) // h
console.log('"aeiou".includes("a")', "aeiou".includes("a")) // true
console.log('"a".includes("aeiou")', "a".includes("aeiou")) // false
Our base case is that the string is empty, so we return 0.
Otherwise, we check if the first character in the string is a vowel (true == 1 and false == 0 in javascript) and sum that with counting the next (smaller by one) string.
You are making two mistakes:
You should have three parameters string , count(count of vowels) and current index i.
You should use includes() instead of comparing character with "aeiouAEIOU"
function countVowels(string,count= 0,i=0) {
if(!string[i]) return count
if("aeiou".includes(string[i].toLowerCase())) count++;
return countVowels(string,count,i+1);
}
console.log(countVowels("abcde")) //2
As asked by OP in comments "Can you please explain why it'sif("aeiou".includes(string[i].toLowerCase())) instead of if(string[i].includes("aeiou".toLowerCase()))"
So first we should know what includes does. includes() checks for string if it includes a certain substring passed to it or not. The string on which the method will be used it will be larger string and the value passed to includes() be smaller one.
Wrong one.
"a".includes('aeiou') //checking if 'aeiou' is present in string "a" //false
Correct one.
"aeiou".includes('a') //checking if 'a' is present in string "aeiou" //true
One possible solution would be:
function countVowels(string, number) {
if (!string) return number;
return countVowels(string.slice(1), 'aeiouAEIOU'.includes(string[0])? number + 1 : number);
}
// tests
console.log('abc --> ' + countVowels('abc', 0));
console.log('noor --> ' + countVowels('noor', 0));
console.log('hi --> ' + countVowels('hi', 0));
console.log('xyz --> ' + countVowels('xyz', 0));
and you should call your function like: countVowels('abc', 0)
Notes about your solution:
you always reset vowelCount inside your function, this usually does not work with recursion.
you defined your function to accept a string, but recall it with an integer in countVowels(vowelCount++); this it will misbehave.
always remember that you have to define your base case first thing in your recursion function, to make sure that you will stop sometime and not generate an infinite loop.
Alternative ES6 solution using regex and slice() method. Regex test() method will return true for vowels and as stated in a previous answer JavaScript considers true + true === 2.
const countVowels = str => {
return !str.length ? 0 : /[aeiou]/i.test(str[0]) + countVowels(str.slice(1));
}
Related
I am attempting to slice my text, in such a way that if the character lenght of a text exceeds the given value, the text should slice from 0 to the value + also go until the next dot is found, so that we avoid slicing a text midsentence.
I've attempted the following
function shorten(text, num)
{
var fulltext=text.toString();
var firstdot = fulltext.indexOf(".");
if (fulltext.length < num)
{
return fulltext;
}
else
{
return fulltext.slice(0, (num + fulltext.indexOf(".")));
}
}
So the logic is, if the text is below the given value, full text is returned, if not i want to slice the text, but also include the text, until next dot.
So once the num value has been reached in characters, it should keep searching until it finds the next dot, and then stop.
Example of what i want given lets say 10 as the num value.
"Hello. This is a test example. This part should be removed."
Should return: "Hello. This is a test example." but instead it will return "Hello. This"
var str = "Hello. This is a test example. This part should be removed."
function shorten(text,num){
let dot = text.indexOf(".",num),
end = dot>=0?dot+1:num
return text.slice(0,end)
}
console.log(shorten(str,6))
some definition
lets say limit num is = 10
indexOf(".",num) since i am taking 10 letters without caring about dot so i have started checking for . after 10 letters that is what second parameter is doing here
dot>=0?dot+1:num this is a short hand which checking if dot is greater than or equals to 0 if its true then return value of dot otherwise return num value
(dot+1) +1 is used to take last dot when slicing without this result will have no dot at end
Please change some code like this
function shorten(text, num)
{
const fulltext=text.toString();
const firstdot = fulltext.indexOf(".", num);
return fulltext.length < num ? fulltext : fulltext.slice(0, firstdot + 1);
}
console.log(shorten("Hello. This is a test example. This part should be removed.", 10));
When I call a recursive on function, where do the results of the call go?
function reverse(str){
if (str.length == 1){
return str;
}
rev = reverse(str.substr(1)) + str.charAt(0);
}
reverse("String");
console.log(rev); // ----> "undefinedS" - Only the last call is saved.
If I just return the value it seems fine. Where is the result getting stored?
function reverse(str){
if (str.length == 1){
return str
}
return reverse(str.substr(1)) + str.charAt(0);
}
reverse("String") // ----> "gnirtS"
Your first version
rev is stored in the global namespace, and is overwritten each time the function reverse is called.
The recursion stops when the string's length is one, after having taken only the last n-1 characters. As a result, the final character is the only character in the string, S, and that is what str.charAt(0) gives.
Since the function reverse does not return a value when str is length 0 (which is what happens with "S".substr(1)) the value of reverse(str.substr(1)) is undefined.
This results in undefinedS.
Your second version
This version creates a call stack whereby the string is slowly taken apart by n-1 (where n is its length) until its length is 1. At that point the stack is unwound, causing each letter from the last to the first to be returned. Each function call as its own Execution context, whereby a Variable Environment is holding the value of each string.
The result of the callstack unwinding is gnirtS.
Cleaner Look code
function reverse(str){
return (str.length == 1)? str : reverse(str.substr(1)) + str.charAt(0);
}
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));
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
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.