I use the following code, in a nodejs app, to build a tree from an array of database rows that form an adjacency list:
// Lay out every node in the tree in one flat array.
var flatTree = [];
_.each(rows, function(row) {
flatTree.push(row);
});
// For each node, find its parent and add it to that parent's children.
_.each(rows, function(row) {
// var parent = _.find(flatTree, function(p) {
// p.Id == row.ParentId;
// });
var parent;
for (var i = 0; i < flatTree.length; i++){
if (flatTree[i].Id == row.ParentId) {
parent = flatTree[i];
break;
}
};
if (parent){
if (!parent.subItems) {
parent.subItems = [];
};
parent.subItems.push(row);
}
});
I expect the commented out _.find call to do exactly the same as what the work-around for loop below it does, but _.find never finds the parent node in flatTree, while the for loop always does.
Similarly, a call to _.filter just doesn't work either, while the substitute loop does:
// var rootItems = _.filter(flatTree, function (node) {
// //node.ParentId === null;
// node.NoParent === 1;
// })
var rootItems = [];
for (var i = 0; i < flatTree.length; i++){
if (flatTree[i].ParentId == null){
rootItems.push(flatTree[i]);
}
}
I am using the underscore-node package, but have tried and had the same results with the regular underscore package.
Just missed the return.
var parent = _.find(flatTree, function(p) {
return p.Id == row.ParentId; // Return true if the ID matches
^^^^^^ <-- This
});
In your code nothing is returned, so by default undefined will be returned and parent will not contain any data.
Related
I am trying to build a function that checks if all fields are populated, if populated then show div if not hide.
I can get this to work on one fields however i have then tried two ways of checking multiple.
first
if first condition met I then ran other condition checking second field nested inside the first... this done not work.
second
I passed in an array of ID's rather than a single... this did not work either..
I am left with a working function that only works if first filed is populated can anyone think of a solution to this or maybe i passed in my array incorrectly.
My code
var myVar = setInterval(myTimer, 10);
function myTimer() {
if(!document.getElementById('Email').value) { // I need this to pass if multiple id's
var divsToHide = document.getElementsByClassName("somediv"); //divsToHide is an array
for(var i = 0; i < divsToHide.length; i++){
divsToHide[i].style.visibility = "hidden"; // or
divsToHide[i].style.display = "none"; // depending on what you're doing
}
}
else {
var divsToHide = document.getElementsByClassName("somediv"); //divsToHide is an array
for(var i = 0; i < divsToHide.length; i++){
divsToHide[i].style.visibility = "visible"; // or
divsToHide[i].style.display = "block"; // depending on what you're doing
}
}
}
Make it so your function takes an argument of the element ID and the class Name you need to check for.
Also, never use .getElementsByClassName() (read here for why). Instead, use .querySelectorAll().
And, you can use the modern .forEach() API of arrays and node lists (not in IE though), which is simpler than managing traditional for loops with indexes.
Lastly, use pre-made CSS classes instead of inline styling.
// You just need to pass the ID and Class to the following line
var myVar = setInterval(function(){ myTimer([id here],[class here]) }, 10);
function myTimer(id, class) {
// Set up these references just once and the rest of the code
// will be easier to read
var elem = document.getElementById(id);
var divsToHide = document.querySelectorAll("." + class);
// Instead of negative logic, reverse the if branches
if(elem.value) {
divsToHide.forEach(function(){
this.classList.remove("hidden"); // Much simpler than inline styling
});
} else {
divsToHide.forEach(function(){
this.classList.add("hidden");
});
}
/* Use pre-made CSS classes instead of inline styling */
.hidden { display:none; }
If you have an array of the IDs such as
let idArray = ['foo', 'bar', 'baz']
You can iterate through an array using a for loop
for (i = 0; i > idArray.length; i++) {
if (!document.getElementById(idArray[i]).value) {
// your hide logic
} else {
// your show logic
}
}
You can create a const of all elements that need to validate. For example,
const elementIdsToBeValidated = ['name', 'email'];
You can also create validator functions that returns true and false based on input,
const nameValidator = (val) => !!val;
const emailValidator = (email) => !!email;
const validators = [nameValidator, emailValidator];
Then you can run your timer function,
var myVar = setInterval(myTimer(['name', 'email']), 10);
function myTimer(ids) {
ids.forEach(id => {
const el = document.getElementById(id);
const val = el.value;
const divEl = document.getElementById('error');
const valid = validators.reduce((acc, validator) => validator(val), false);
if(valid) {
divEl.style.display = 'none';
} else {
divEl.style.display = 'block';
}
});
}
You can look at this stackBlitz example,
https://stackblitz.com/edit/js-ie7ljf
I have a page that displays a list of file templates built using the following method.
var loadCustomTemplate = function () {
loadBaseTemplate();
var res = 0;
for (i = 0; i < self.GetSeam().length; i++) {
var a = self.count() + 1;
self.count(a);
res = self.GetSeam()[i].FileFormat.split("_");
if (res.length == 4) {
var ap = res[3].split('.');
self.append(ap[0]);
} else {
self.append("");
}
var obj = {
Code: ko.observable(self.code()),
Number: ko.observable(self.number()),
SeamReportPath: ko.observable(self.reportPath()),
FileFormat: ko.observable(self.append()),
SequenceNumber: ko.observable(a)
}
self.CustomTemplate.push(obj);
}
self.count(0);
};
The user is then allowed to edit the fields as needed. They can also add records or remove them as needed. The method to add a record is as follows.
self.addTemplate = function () {
var count = self.CustomTemplate().length + 1;
var obj = {
Code: ko.observable(self.code()),
Number: ko.observable(self.number()),
SeamReportPath: ko.observable(self.reportPath()),
FileFormat: ko.observable(""),
SequenceNumber: ko.observable(count)
}
self.CustomTemplate.push(obj)
};
Once those updates are made they can save the updated CustomTemplate. This uses ajax that is not important to this question. The save method calls a validation method that is supposed to check to make sure there are no duplicate FileFormat fields in the object array. This is what I have, but it is failing.
var validateTemplates = function() {
for (i = 0; i < self.CustomTemplate().length; i++) {
var checkVal = self.CustomTemplate()[i].FileFormat;
var checkSeq = self.CustomTemplate()[i].SequenceNumber;
for (j = 0; j < self.CustomTemplate().length; j++) {
if (checkSeq !== self.CustomTemplate()[j].SequenceNumber ){
if (checkVal+"" === self.CustomTemplate()[j].FileFormat) {
if (checkSeq == self.CustomTemplate()[j].SequenceNumber ){
return false;
}
}
}
}
return true;
};
The problem is that when checking self.CustomTemplate()[i].FileFormat and self.CustomTemplate()[i].SequenceNumber it isn't reflecting the data displaying on the page or the data being sent to the controller (MVC 4). If I put either of those in an alert it is showing a function. How do I access the data in those specific fields for comparison?
Thanks in advance.
If I put either of those in an alert it is showing a function.
That's because you're doing this kind of thing:
var checkVal = self.CustomTemplate()[i].FileFormat;
FileFormat is the result of ko.observable(...), which returns a function, so checkVal in fact does contain a function.
The solution is for all those cases to do this:
var checkVal = self.CustomTemplate()[i].FileFormat(); // Parentheses at the end!
The parentheses execute the observable function, and if you do so without parameters you "get" the value of that observable. (If you would pass in a value, it would "set" the observable to that value.)
I have the next object:
var persons= {};
persons["Matt"].push("A");
persons["Matt"].push("B");
persons["Matt"].push("C");
And I want to know if the object contains the element which I try to insert.
E.g:
persons["Matt"].push("A"); /* The element A already exist... And I don't want duplicate elements.*/
Anybody know one way to make it?
EDIT WITH MORE DETAILS:
I have a the next code:
function insertIfNotThere(array, item) {
if (array.indexOf(item) === -1) {
array.push(item);
}
}
function EventManager(target) {
var target = target || window, events = {};
this.observe = function(eventName, cb) {
if (events[eventName]){
insertIfNotThere(events[eventName], cb);
}else{
events[eventName] = []; events[eventName].push(cb);
}
return target;
};
this.fire = function(eventName) {
if (!events[eventName]) return false;
for (var i = 0; i < events[eventName].length; i++) {
events[eventName][i].apply(target, Array.prototype.slice.call(arguments, 1));
}
};
}
I use your method for checking if the element with the content indicated exist. But... It push the element ever... I don't know what's happening...
First things first. When you do
persons= {};
you are creating a global property called persons and assigning an empty object to it. You might want a local variable here. So, change it to
var persons = {};
And then, when you create a new key in the object, by default, the value will be undefined. In your case you need to store an array. So, you have to initialize it like this
persons['Matt'] = [];
and then you can use the Array.prototype.indexOf function to find out if the item being added is already there in the array or not (it returns -1 if the item is not found in the array), like this
if (persons['Matt'].indexOf("A") === -1) {
persons['Matt'].push("A");
}
if (persons['Matt'].indexOf("B") === -1) {
persons['Matt'].push("B");
}
You can create a function to do this
function insertIfNotThere(array, item) {
if (array.indexOf(item) === -1) {
array.push(item);
}
}
var persons = {};
persons['Matt'] = [];
insertIfNotThere(persons['Matt'], 'A');
insertIfNotThere(persons['Matt'], 'B');
// This will be ignored, as `A` is already there in the array
insertIfNotThere(persons['Matt'], 'A');
Use indexOf to check for the existence of A. If it doesn't exist (is -1), add it to the array:
if (persons['Matt'].indexOf('A') === -1) {
persons['Matt'].push('A');
}
I'm new to javascript and am not real capable with the asynchronous aspects, closures, etc. I have done a few days research on this and lots of trial & error but can't seem to get past my issue, which is:
I am trying to walk a tree structure, gathering all the bottom level nodes (those without child nodes). This node data gets loaded into global arrays (not optimal but needed). The walk function I'm using is recursive. But the asynchronous nature cause the first call to the function to return before the recursive calls return, so the complete tree doesn't get interrogated. I tried putting it in an anonymous function which seems to get the entire tree traversed, but then the global arrays are not being loaded (inaccessible?).
BTW, the real code is on a separate, isolated network so a direct cut & paste to here is not possible. Below is functional equivalent of the relevant parts (unless I've made a typo). Apologies for that. Any help would be appreciated.
var nodeList = new Array(); // global variable
function someFunction(rootNode) {
// unrelated processing here
walkTree(rootNode); // gather the childless nodes
return;
}
function walkTree(node) {
return function() { // required in order traverse the entire tree
// but with it, nodeList does not get populated
var num = node.numChildren();
var childNodes = node.getChildNodes();
for (var i=0; i<num; i++) {
var currentNode = childNodes.item(i);
if (currentNode.numChildren() > 0) {
walkTree(currentNode);
}
else {
var obj = new Object();
/// extract certain attributes of current node here
/// and make a variant
nodeList[nodeList.length] = obj;
}
} // END for
} // close anonymous function
} // END FUNCTION
If you don't need asynchronous execution, your code can be simplified:
var nodeList = [];
function someFunction(rootNode) {
// unrelated processing here
walkTree(rootNode); // gather the childless nodes
return;
}
function walkTree(node) {
var num = node.numChildren(),
childNodes = node.getChildNodes();
for (var i=0; i<num; i++) {
var currentNode = childNodes.item(i);
if (currentNode.numChildren() > 0) {
walkTree(currentNode);
}
else {
var obj = new Object();
/// extract certain attributes of current node here
/// and make a variant
nodeList.push(obj);
}
}
}
If you do need asynchronous execution, the implementation would depend on what asynchronous mechanism you used (web workers, simulation using setTimeout, a framework like Clumpy, etc.).
With Clumpy, for instance, you might code it like this (untested):
var nodeList = [],
clumpy = new Clumpy();
function someFunction(rootNode) {
// unrelated processing here
walkTree(rootNode); // gather the childless nodes
return;
}
function walkTree(node) {
var num = node.numChildren(),
childNodes = node.getChildNodes(),
i;
clumpy.for_loop(
function() { i = 0; },
function() { return i < num; },
function() { i++; },
function() {
var currentNode = childNodes.item(i);
if (currentNode.numChildren() > 0) {
walkTree(currentNode);
}
else {
var obj = new Object();
/// extract certain attributes of current node here
/// and make a variant
nodeList.push(obj);
}
}
);
}
I have an js, object which is something like this:
function test{
this.variable = {};
this.populate = function(){
// do some crap....
// and i populate the object like this
this.variable{xyz..} = new object();
}
this.outputThecrap(){
for (var key in data) {
if (data.hasOwnProperty(key)) {
if(data[key].idParent != '0'){
//do some stuff
}
}
}
}
this.addSomeOnBeginigQQ(){
// how do i do that!!!!Q_Q
this.variable{blabla...} = new blabla();
}
}
now after I populate the object like
var t = new test();
t.populate();
t.addSomeOnBegining();
t.outputThecrap();
I get the problem that the added properties wind up on the end of the loop ... and I need them to be on the top
Anyone has some idea how to solve this?
UPDATE:
The structure of the object is not open to change. i cant use the array as a container either, that s out of question.
If you want a stack, you will need to use an Array - a list with a defined order. Object properties have none in JavaScript, there is nothing like "associative arrays". Also, you should the prototype.
You can set properties of array just as you do with objects, but the property names need to be numerically (i.e. integers). You then loop over them with a for-loop. Array objects also have some extra methods, for example to add items in the beginning or the end (which I have used below):
function Test() {
this.data = []; // an array
}
Test.prototype.populate = function(){
// populate the array like this
this.data.push({…});
};
Test.prototype.outputThecrap = function(){
for (var i=0; i<this.data.length; i++) {
var item = this.data[i];
if (item /* has the right properties*/)
//do some stuff
}
};
Test.prototype.addSomeOnBeginning(){
this.data.unshift({…});
};
Then use it like this:
var t = new Test();
t.populate();
t.addSomeOnBeginning();
t.outputThecrap();
The "ordered key array" looks like this:
function Test() {
this.data = {}; // the object
this.order = []; // an array
}
Test.prototype.populate = function(){
this.data["something"] = {…}
this.order.push("something");
};
Test.prototype.addSomeOnBeginning(){
this.data["other"] = {…};
this.order.unshift("other");
};
Test.prototype.outputThecrap = function(){
for (var i=0; i<this.order.length; i++) {
var key = this.order[i],
item = this.data[key];
if (item && key /* fulfill your requirements */)
// do some stuff
}
};