Traversing nodes: Why check if node was included already? - javascript

In this breadth first traversal of a DOM, what is the purpose of visitedNodes array and how
does !visitedNodes.includes(currentNode) work? What if I have 2 identical div's with the same class? Wouldn't that check skip the 2nd div since it's identical? That doesn't seem like ideal behavior, right?
function bfs() {
const nodesToVisit = [];
const visitedNodes = [];
nodesToVisit.push(document.getElementsByClassName("a")[0]);
const log = document.getElementById("results");
while (nodesToVisit.length > 0) {
let currentNode = nodesToVisit.pop(); // from the front!
if (currentNode && !visitedNodes.includes(currentNode)) {
visitedNodes.push(currentNode);
if (currentNode.nodeType == 1) {
let logItem = document.createElement("p");
let logText = document.createTextNode(currentNode.className);
logItem.appendChild(logText);
log.appendChild(logItem);
}
[].slice.call(currentNode.childNodes).reverse()
.forEach(function(node) {
nodesToVisit.push(node);
})
}
}
}

Related

Issue with recursive function converting dom element to json

I have a recursivce function that takes a dom tree and converts it to JSON.
However I want to exclude any nodes that have a specific data attribute data-exclude
const htmlToJSON = node => {
const exclude = node.attributes?.getNamedItem('data-exclude');
if (!exclude) {
let obj = {
nodeType: node.nodeType
};
if (node.tagName) {
obj.tagName = node.tagName.toLowerCase();
} else if (node.nodeName) {
obj.nodeName = node.nodeName;
}
if (node.nodeValue) {
obj.nodeValue = node.nodeValue;
}
let attrs = node.attributes;
if (attrs) {
length = attrs.length;
const arr = (obj.attributes = new Array(length));
for (let i = 0; i < length; i++) {
const attr = attrs[i];
arr[i] = [attr.nodeName, attr.nodeValue];
}
}
let childNodes = node.childNodes;
if (childNodes && childNodes.length) {
let arr = (obj.childNodes = []);
for (let i = 0; i < childNodes.length; i++) {
arr[i] = htmlToJSON(childNodes[i]);
}
}
return obj;
}
};
const parser = new DOMParser();
const { body } = parser.parseFromString(page, 'text/html');
let jsonOutput = htmlToJSON(body);
console.log(jsonOutput);
I am clearly missing something with the way I am excluding because when I log the results it is returning undefined instead of just excluding it.
It's most likely because you're not returning anything from htmlToJSON in the case of "exclude == true". Notice how your lambda function doesn't have a "return " in that case. So the function will by default return "undefined."
And if you fill an array element with "undefined" it becomes a sparse array. So those elements in the array with "undefined" values become interpreted as "empty" slots by console.log() when printing the contents of any array to the console.
Update: I tried your code and, yup, my explanation above is correct. However, if you don't care about implicitly returning undefined from your htmlToJSON(), then you can just modify your inner for loop:
for (let i = 0; i < childNodes.length; i++) {
let json = htmlToJSON(childNodes[i]);
json && arr.push(json);
}
This way, only if json is truthy, will it add an element to the arr array.
I tried this code with your original function, and also with a modified version that returns null if exclude == true, and both ways worked.
Here's a Codepen example.
Did not execute the code. As far as I can see htmlToJSON will return obj or undefined. If exclude is truthy, the function will return undef, thats what you are seeing.
Change your for loop:
for (let i = 0, temp; i < childNodes.length; i++) {
temp = htmlToJSON(childNodes[i]);
temp && (arr[i] = temp);
}
that way you make sure if temp is defined you assign, otherwise not.
Another option is to use Array.prototype.filter on the resultant array.

Javascript - Merge two sorted linked lists understanding

I'm trying to figure out why in the following function, at certein point they do mergedTail.next = temp; mergedTail = temp; Instead of to have only mergedTail = temp;:
let merge_sorted = function(head1, head2) {
// if both lists are empty then merged list is also empty
// if one of the lists is empty then other is the merged list
if (!head1) {
return head2;
} else if (!head2) {
return head1;
}
let mergedHead = null;
if (head1.data <= head2.data) {
mergedHead = head1;
head1 = head1.next;
} else {
mergedHead = head2;
head2 = head2.next;
}
let mergedTail = mergedHead;
while (head1 && head2) {
let temp = null;
if (head1.data <= head2.data) {
temp = head1;
head1 = head1.next;
} else {
temp = head2;
head2 = head2.next;
}
mergedTail.next = temp;
mergedTail = temp;
}
if (head1) {
mergedTail.next = head1;
} else if (head2) {
mergedTail.next = head2;
}
return mergedHead;
};
Many thanks.
mergedTail always refers to the last element of the merged list. temp is set to the element that should be appended to that list next.
By setting mergedTail.next = temp, the element temp is appended to the merged list. But now mergedTail no longer refers to the last element, but to the second-to-last element. So we assign mergedTail = temp to move it one step ahead, and maintain our invariant.
I would personally have preferred mergedTail = mergedTail.next, which is equivalent and maybe slightly less efficient, but makes the intent clearer.

Copy List with Random Pointer - Copy not point to -

I'm trying to solve the problem below:
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.
The Linked List is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:
val: an integer representing Node.val
random_index: the index of the node (range from 0 to n-1) where random pointer points to, or null if it does not point to any node.
Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]
My idea was loop the head and create a new node with the same value and set it to my ans Node. It will be a copy because I'm creating a new one. Then, I loop again for the random pointer which looks in my ans Node the node with the value that I should point to. It works perfectly for the input above. However, if I have an input with duplicate values, the findeNode method might return an invalid Node. For example it fails for the input below:
[[3,null],[5,17],[4,null],[-9,6],[-10,3],[5,15],[0,11],[6,null],[-6,16],[3,16],[-6,11],[9,12],[-2,1],[-3,11],[-1,10],[2,11],[-3,null],[-9,7],[-2,4],[-8,null],[5,null]]
My output:
[[3,null],[5,3],[4,null],[-9,null],[-10,3],[5,15],[0,11],[6,null],[-6,13],[3,13],[-6,11],[9,12],[-2,1],[-3,11],[-1,8],[2,11],[-3,null],[-9,7],[-2,4],[-8,null],[5,null]]
Correct output:
[[3,null],[5,17],[4,null],[-9,6],[-10,3],[5,15],[0,11],[6,null],[-6,16],[3,16],[-6,11],[9,12],[-2,1],[-3,11],[-1,10],[2,11],[-3,null],[-9,7],[-2,4],[-8,null],[5,null]]
Node definition:
function Node(val, next, random) {
this.val = val;
this.next = next;
this.random = random;
};
My code:
var copyRandomList = function(head) {
if(!head) return head;
let ans = new Node(0);
let temp = ans;
let cur = head;
while(cur) {
const node = new Node(cur.val);
temp.next = node;
cur = cur.next;
temp = temp.next;
}
cur = head;
temp = ans.next;
while(cur) {
const randomNode = cur.random;
if(!randomNode) {
temp.random = null;
} else {
const node = findNode(ans, randomNode.val);
temp.random = node;
}
cur = cur.next;
temp = temp.next;
}
return ans.next;
};
const findNode = (list, val) => {
if(!val)
return null;
while(list) {
if(list.val === val)
return list;
list = list.next;
}
return null;
}

Linked List remove method by value with Javascript

Trying to get this linked list remove method to work. should remove a node from the middle if that is where the value is found, and preserve the rest of the sequence. should not modify the list sequence if the last node is the node to be removed. it also should replace the start node with the second node when the first node value is x and the remove function is called with the value x.
remove(valueToRemove) {
let currentNode = this.firstNode;
let previousNode;
let foundValue = valueToRemove = currentNode.value;
while (!foundValue) {
previousNode = currentNode;
currentNode = currentNode.next;
if (!currentNode) {
return;
}
foundValue = valueToRemove === currentNode.value;
}
let nextNode = currentNode.next;
if (currentNode === this.firstNode) {
this.firstNode = nextNode;
} else {
previousNode.next = nextNode;
}
this.listSize--;
}
In the below code you are equating the values hence overriding the valueToRemove
let foundValue = valueToRemove = currentNode.value;
change it to
let foundValue = valueToRemove === currentNode.value;

How to break a loop by setting a variable?

I wrote an ajax call with two for loop.
The first cycle looks for a key whose value is "city", the second one search for a key whose value is "hamlet".
A table is displayed if any of these two values is found.
I want to set a new variable var valueFound to indicate that the values in the first cycle have already been found, in order to say "if the value in the first cycle has been found, don't search for the second value in the other for loop".
What I want is avoiding duplicates in the search results in case both of the values are found.
The code I wrote so far is the following:
success: function (data) {
for (let i = 0; i < data.features.length; i++) {
let typeCity = data.features[i].properties.geocoding.type;
if (typeCity === "city") {
let nameCity = data.features[i].properties.geocoding.name;
for (let i = 0; i < francigena.tappe.length; i++) {
let tappa = francigena.tappe[i];
let city = francigena.tappe[i].city;
let fs = francigena.tappe[i].fs;
if (city === nameCity) {
$('#tabellaEconteuti').show();
}
};
}
else if (typeCity === "hamlet") {
let nameCity = data.features[i].properties.geocoding.name;
for (let i = 0; i < francigena.tappe.length; i++) {
let tappa = francigena.tappe[i];
let city = francigena.tappe[i].city;
let fs = francigena.tappe[i].fs;
if (city === nameCity) {
$('#tabellaEconteuti').show();
}
};
};
}
},
When in the JSON array typeCity === "city" or typeCity === "hamlet" no problem, BUT it both of the values are found I get duplicates in the search results.
How can I set such a variable var valueFound to indicate that some values have already been found and breaking the loop?
success: function (data) {
cont featuresMapByCityType = dataFeaturesMapByCityType(data.features);
cont tappesMapByCityType = getTappeMapByCity(francigena.tappe);
let feature = featuresMapByCityType['city'] || featuresMapByCityType['hamlet'];
if (feature) {
let nameCity = feature.properties.geocoding.name;
cont tappe = getTappeMapByCity[nameCity];
if (tappe && tappe.city === nameCity) {
$('#tabellaEconteuti').show();
}
}
};
function dataFeaturesMapByCityType(features) {
let featuresMapByCityType = {};
features = features || [];
features.forEach(feature => {
featuresMapByCityType{feature.properties.geocoding.type} = feature;
});
return featuresMapByCityType;
}
function getTappeMapByCity(tappes) {
let tappesMapByCityType = {};
tappes = tappes || [];
tappes.forEach(tappe => {
tappesMapByCityType{tappe[i].city} = tappe;
});
return tappesMapByCityType;
}
I have organized the code and removed multiple for loops, not sure the francigena comes from. this is a rough organization of code. This might help you.

Categories

Resources