recursion not working without declaring global variable - javascript

Why does version A work but version B doesn't? How can I make version B work without declaring a global variable outside the function (which is bad practice)?
I'm not clear on why I can't just declare count inside the function itself.
A)
var count = 0;
var containsFiveOrMoreDivs = function(domElement) {
if (domElement && domElement.tagName === "DIV") {
count++;
}
//base case:
if (count >= 5) {
return true;
} else {
if (domElement.hasChildNodes()) {
var children = domElement.childNodes;
for (var i = 0; i < children.length; i++) {
if (containsFiveOrMoreDivs(children[i])) {
return true;
}
}
}
return false;
}
};
B)
var containsFiveOrMoreDivs = function(domElement) {
var count = 0;
if (domElement && domElement.tagName === "DIV") {
count++;
}
//base case:
if (count >= 5) {
return true;
} else {
if (domElement.hasChildNodes()) {
var children = domElement.childNodes;
for (var i = 0; i < children.length; i++) {
if (containsFiveOrMoreDivs(children[i])) {
return true;
}
}
}
return false;
}
};

What you really need is two functions, one inside the other:
function containsFiveOrMoreDivs(domElement) {
var count = 0;
function doCount(domElement) {
if (domElement && domElement.tagName === "DIV") {
count++;
}
//base case:
if (count >= 5) {
return true;
}
else {
if (domElement.hasChildNodes()) {
var children = domElement.childNodes;
for (var i = 0; i < children.length; i++) {
if (doCount(children[i])) {
return true;
}
}
}
return false;
}
}
return doCount(domElement);
}
In that setup, you pass in an element reference, and then the outer function calls the inner function after initializing the counter.
original not very good answer here
Your second version ("B") has "count" as a local variable of the function. Each invocation of the function gets its very own "count" variable, and in each invocation the first thing that happens is that it's initialized to zero.
If you don't want a global, you can use a closure:
var containsFiveOrMoreDivs = function() {
var count = 0;
return function(domElement) {
if (domElement && domElement.tagName === "DIV") {
count++;
}
//base case:
if (count >= 5) {
return true;
} else {
if (domElement.hasChildNodes()) {
var children = domElement.childNodes;
for (var i = 0; i < children.length; i++) {
if (containsFiveOrMoreDivs(children[i])) {
return true;
}
}
}
return false;
}
};
}();
That code wraps your actual counter function in an anonymous function that includes the "count" variable. It won't be global; it'll be completely private to the "containsFiveOrMoreDivs" function. This is like the best of both worlds: you get to treat "count" as a global, but it's not global. You don't need to worry about carrying a parameter around either.

Variables in Javascript exist in function scope. Every time you call containsFiveOrMoreDivs, count will always be 0 in your version B. Hence, infinite recursion.
What you can do, however, is pass in 'count' each time you call from within the function, and use that (ensuring it's initialised correctly the first time):
var containsFiveOrMoreDivs = function(domElement, count) {
if (!count) {
count=0;
}
if (domElement && domElement.tagName === "DIV") {
count++;
}
//base case:
if (count >= 5) {
return true;
} else {
if (domElement.hasChildNodes()) {
var children = domElement.childNodes;
for (var i = 0; i < children.length; i++) {
if (containsFiveOrMoreDivs(children[i], count)) {
return true;
}
}
}
return false;
}
};
Call it just like you currently are (containsFiveOrMoreDivs('elementname');)

Version B will not work because each time the function is called the counter is redeclared, so counter never increments.

Your recursive function needs to consume count as an argument. The way you have it will initialize count to 0 no matter how many times you recurse.
Here's an example of a recursive function that consumes "the number of times to do something" as a parameter. Modify it to support your case. Your base case would be something like "count is greater than 5", and each time you call recursively, you add 1 to the count you provide to the recursive call.
function executeMany(fn, count) {
if (count > 0) {
fn();
executeMany(fn, count - 1)
}
}
// this logs "Test" to the console twice
executeMany(function() { console.log("Test"); }, 2);

You could define the function with a count parameter and pass an initial value or if you are using a ECMA 16 you could set a default value for the parameter by doing count=0.
var containsFiveOrMoreDivs = function(domElement, count) {
if (domElement && domElement.tagName === "DIV") {
count++;
}
//base case:
if (count >= 5) {
return true;
} else {
if (domElement.hasChildNodes()) {
var children = domElement.childNodes;
for (var i = 0; i < children.length; i++) {
if (containsFiveOrMoreDivs(children[i]), count) {
return true;
}
}
}
return false;
}
};
// call function and set counter to some initial value, such as zero
containsFiveOrMoreDivs(domElement, 0);

Related

Parameter returns undefined

I am trying to write a function that identifies if a word is an isogram or not. This is what I have done so far:
function isIsogram(word) {
var result;
var counter = 0;
var dubs = 0;
if (word.length === 0) {
result = false;
} else {
var lower = word.toLowerCase();
var array = Array.from(lower);
for (i = 0; i < array.length; i++) {
counter++;
for (j = i + 1; j < array.length; j++) {
if (array[i] === array[j]) {
dubs++;
}
}
}
if ((counter > 0) && (dubs === 0)) {
result = true;
} else if ((counter > 0) && (dubs > 0)) {
result = false;
}
}
console.log(result);
return result;
}
isIsogram("word");
When I run the above code in my browser's javascript console, it works pretty well. But when I post it onto the environment where I am being tested, it gives an error that "word" (the parameter) is undefined.
I even tried hard coding a parameter by declaring a string value for word outside the function, it still said undefined. What am i not doing right?
Seems to work as far as I can see.
Can you provide information about how you call your function isIsogramm('teststring');?
https://jsfiddle.net/TobiObeck/z15eos81/

How to make recursive function on javascript without Maximum call stack size exceeded error?

I would like to make the same robot walk function with javascript but it get call stack size error.
http://www.chegg.com/homework-help/questions-and-answers/robot-take-steps-1-2-3-meters-write-program-allows-shows-possible-steps-robot-take-using-r-q3756383
function walk(meter) {
if(meter < 0) {
count = 0;
} else if(meter <= 2) {
count = meter;
} else if(meter == 3) {
count = walk(meter-1)+walk(meter-2)+1;
} else {
count = walk(meter-1)+walk(meter-2)+walk(meter-3);
}
return count;
}
console.log(walk(100));
It would exceed call stack size since your complexity is exponential, you can use memoization to solve your problem which helps covert an exponential time complexity into a polynomial one, now this code runs in O(100)
let obj = {};
function walk(meter) {
if(meter < 0) {
count = 0;
}
else if(obj[meter] != undefined){
return obj[meter];
} else if(meter <= 2) {
count = meter;
} else if(meter == 3) {
count = walk(meter-1)+walk(meter-2)+1;
} else {
count = walk(meter-1)+walk(meter-2)+walk(meter-3);
}
obj[meter] = count;
return count;
}
console.log(walk(100));

refactoring working recursion code (hasFiveDIVs) for traversing the DOM

#oriol provided an amazing two line recursive solution for a problem I was working on today.
function numOccurencesRecursive(arr, val) {
if (!arr.length) return 0;
return (arr[0] === val ? 1 : 0) + numOccurencesRecursive(arr.slice(1), val);
}
I was inspired to refactor some spagetti-ish code that I wrote yesterday:
//returns a boolean
function containsFiveOrMoreDivs(domElement) {
var count = 0;
function doCount(domElement) {
if (domElement && domElement.tagName === "DIV") {
count++;
}
if (count >= 5) {
return true;
}
if (domElement.hasChildNodes()) {
var children = domElement.childNodes;
for (var i = 0; i < children.length; i++) {
if (doCount(children[i])) {
return true
}
};
};
return false;
}
return doCount(domElement)
}
containsFiveOrMoreDivs(document);
Here's the attempt:
function containsFiveOrMoreDivsPureRecursion(domElement) {
if (!domElement && domElement.tagName !== "DIV") {
return 0;
}
return (domElement.tagName === "DIV" ? 1 : 0) + containsFiveOrMoreDivsPureRecursion(domElement.childNodes()); //?
}
how would I loop through all the child nodes recursively in this version? Is what I'm doing possible?
With recursion, you should remember to return 1 type of value, however you want to return a boolean yet you want your function to return count too. This really complicates things.
Heres a recursive function to count the element types
function countElements(domElement, elementType) {
count = (domElement && domElement.tagName === elementType.toUpperCase());
if (domElement.hasChildNodes()) {
for (var i = 0; i < domElement.childNodes.length; i++) {
count += countElements(domElement.childNodes[i], elementType);
};
};
return count;
}
you can use it like this to achieve what you want.
countElements(document, 'div') >= 5
However, this might not be the most efficient way of doing it since it will traverse the whole DOM tree but what you really want is for it to stop when you've hit 5 elements.
function containsFiveOrMoreDivsPureRecursion(domElement, elementType) {
var count = 0;
function countElements(domElement, elementType) {
count += (domElement && domElement.tagName === elementType.toUpperCase());
if (count >= 5) return count;
if (domElement.hasChildNodes()) {
for (var i = 0; i < domElement.childNodes.length; i++) {
countElements(domElement.childNodes[i], elementType);
};
};
return count;
}
return countElements(domElement, elementType) >= 5;
}
In this example, you will need to keep a variable outside of your function (thus it's not really a pure recursion) to keep track of the count and be able to return a boolean.

Euler 7 Javascript

Can someone help with this code? It's supposed to get the 10,001th prime number. I know the is_prime function works to test if a number is prime since I successfully utilized this code for a previous problem. Now I'm just trying to call that in a for loop until counter hits what I want, while storing the most recent number into a variable 'holder' and printing holder at the end.
function is_prime(num) {
if (isNaN(num)) return false;
for (var i=2; i<=Math.sqrt(num); i++) {
if (num % i === 0) {
return false;
}
}
return true;
}
function getBigPrime () {
var holder = 0;
var counter = 0;
for (var k=3; counter<=10000; k+=2) {
if (is_prime(k))
holder = k;
counter += 1;
}
console.log(holder);
}
getBigPrime();
If you omit the brackets for an if block, only the first line will actually be part of your block. Your current if statement behaves like this:
if (is_prime(k)) {
holder = k;
}
counter += 1;
Also, your loop skips 2, the first prime number.
You have a scoping error with counter.
For your for loop you can initialize counter = 1; to account for 2 and leave as is
http://jsfiddle.net/XtTYm/2/
function is_prime(num) {
if (isNaN(num)) return false;
var sq = Math.sqrt(num);
for (var i=2; i<=sq; i++) {
if (num % i === 0) {
return false;
}
}
return true;
}
function getBigPrime () {
var holder = 0;
var counter = 1;
for (var k=3; counter<=10000; k+=2) {
if (is_prime(k)){
holder = k;
counter += 1; // should be inside the if
}
}
console.log(holder);
}
getBigPrime();

Test if object with attribute exists in an array using jQuery

Can someone please tell me the best way to check if an object within an array of objects has a type of 11?
Below is what I have but it will alert for every object in the array, can I check the whole array and get it to alert just the once at the end?
I've seen methods like grep but I've been trying and can't get it to work. I'm using jQuery.
var x;
for (x = 0; x < objects.length; x++) {
if (objects[x].type == 11) {
alert("exists");
} else {
alert("doesnt exist");
}
}
Best way is use Array.some:
var exists = objects.some(function(el) { return el.type === 11 });
In the link there is also a shim for the browser that doesn't support it.
Otherwise you can just iterate:
var exists = false;
for (var i = 0, el; !exists && (el = objects[i++]);)
exists = el.type === 11;
Once you have the exists variable set, you can just do:
if (exists) {
// do something
}
Outside the loop, in both cases.
Your code should actually do it. If you're bothered that the loop will continue uselessly, you can abort it by calling break;
if(objects[x].type == 11){
alert("exists");
break;
}
Make it a function:
function hasObjWithType11(arr) {
var x;
for (x = 0; x < arr.length; x++) {
if(arr[x].type == 11){
return true;
}
}
return false;
}
alert(hasObjWithType11([{type:1}, {type:11}]); // alerts true
This will do it
var exists = false;
for (var x = 0; x < objects.length; x++) {
if(objects[x].type == 11){
exists = true;
break;
}
}
if(exists){
alert("exists");
}
You could make the searching code more reusable by wrapping it into a separate function. That way you can externalize the condition for easier reading:
function array_contains(a, fn)
{
for (i = 0, len = a.length; i < len; ++i) {
if (fn(a[i])) {
return true;
}
}
return false;
}
if (array_contains(objects, function(item) { return item.type == 11; })) {
alert('found');
}
You could also use Array.some():
if (objects.some(function(item) {
return item.type == 11;
})) {
alert('exists');
}
For IE < 9, refer to the MDN documentation for a mock version.

Categories