refactoring working recursion code (hasFiveDIVs) for traversing the DOM - javascript

#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.

Related

Using forEach Loop Generating prime number in Javascript between 1 to 50 [duplicate]

This question already has answers here:
Check Number prime in JavaScript
(47 answers)
Closed 2 years ago.
Here's my code but my answer is not that which i want..
Please Check This and give me a solution to get prime number using Foreach Loop b/w 1-50
Thanks In Advance :)
function isPrime(num) {
for ( var i = 2; i < num; i++ ) {
if ( num % i === 0 ) {
return false;
}
}
return true;
}
var txt = "";
function shown(n) {
var arr = [2];
arr.forEach(myFunction);
document.getElementById("foreach").innerHTML = txt;
// document.getElementById('forLoop').innerHTML = arr; // use arr result on your own
}
function myFunction(arr, index, array) {
var i;
var arr = [2];
if ( isPrime(i) ) {
arr.push(i);
}
txt += arr + "<br>";
}
shown(50);
This is probably a too-advanced answer for a homework of this level, but technically it follows the rules (use Array.forEach) and it works.
The primes() generates new primes based on previous primes. So it won't test the reminder of all integers, thus more effecient. There are several arrow function uses, too, to keep things short. If you indeed use this answer, please try to read the relevant documentations and learn:
Iterators and Generators
Arrow function expressions
for...of
Template literals
Seriously, try to think step-by-step. That's how you learn anything.
function* primes() {
const previous = [];
for (let i = 2; true; i++) {
let isPrime = true;
for (let p of previous) {
if (i % p === 0) {
isPrime = false;
break;
}
}
if (isPrime) {
previous.push(i);
yield i;
}
}
}
function* takeUntil(cb, iter) {
for (let val of iter) {
if (cb(val)) {
return;
}
yield val;
}
}
function showArrayIn(arr, container) {
arr.forEach(p => container.innerHTML += `${p},<br/>`); // technically, we used Array.forEach.
}
showArrayIn(
// get the prime number array declarativly
Array.from(takeUntil(n => n >= 50, primes())),
// show in the container specified
document.getElementById("results")
);
Primes:
<div id="results"></div>
function primeFactorsTo(max)
{
var store = [], i, j, primes = [];
for (i = 2; i <= max; ++i)
{
if (!store [i])
{
primes.push(i);
for (j = i << 1; j <= max; j += i)
{
store[j] = true;
}
}
}
return primes;
}
console.log(primeFactorsTo(5));
console.log(primeFactorsTo(15));
I Think so this is the correct answer which i deserve..
It is code lover short and aggressive
function primes(limit)
{
var prime=[], i=1;
while (++i < limit+1) prime.reduce((a,c)=>(i%c)*a,1) && prime.push(i);
prime.unshift(2);
return prime;
}
[50].forEach(n=>document.getElementById('foreach').innerHTML=(`${primes(n)}`));
Consider the following example.
function isPrime(num) {
if (num === 1) {
return false;
} else if (num === 2) {
return true;
} else {
for (var x = 2; x < num; x++) {
if (num % x === 0) {
return false;
}
}
return true;
}
}
function shown(n) {
var list = [];
for (var i = 1; i <= n; i++) {
list.push(i);
}
list.slice().reverse().forEach(function(n, k, o) {
if (!isPrime(n)) {
list.splice(o.length - 1 - k, 1);
}
});
document.getElementById("show").innerHTML = list;
}
shown(50);
Prime: <p id="show"></p>

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/

recursion not working without declaring global variable

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);

checking multiple elements in javascript array

I have an array 'type' with multiple elements.how to check two elements contains in 'type' array?
i have tried the below code
var i, j;
var type = [];
for (i = 1; i <= count; i++)
{
var filetype_value = parseInt((document.submission['select_type_' + i].value));
type.push(filetype_value);
}
function search(arg)
{
for (j = 1; j <= count; j++)
{
if (type[j] === arg)
{
return true;
}
else
{
return false;
}
}
}
if(search(1) && search(2))
{
alert("Contains in array")
}
There is some problems with your approach.
1) You are just checking if type[1]===arg it will return true otherwise it will return false. So it will just check for type[1].
2) because file_type value is string so filetype_value' === arg is never going to be true.
3) In your loop j=1 will never check for first element of array.
Try this
function search(arg){
var matched = false;
for (j = 0; j <= type.length; j++){
if (type[j] == arg){
matched = true;
break;
}
}
return matched;
}
You have 2 problems
You are pushing the string "filetype_value" onto your array and not the actual value so in your search function you are actually testing: 'filetype_value' === arg
You are starting your loop using 1, array's start at an index of 0
change
type.push('filetype_value');
to
type.push(filetype_value);
change
for (j = 1; j <= count; j++)
to
for (j = 0; j <= count; j++)
Also instead of doing a loop you can use the array indexOf method to test if a value is in the array
function search(arg){
return type.indexOf(arg) !== -1;
}

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