nodejs declare array of objects with dynamic size - node.js

Here's how I declare my array of objects:
let arrayOfObjects = [{ _id:0, names:'' }];
And I want to assign the values like below:
for (var i = 0; i < kelasList.length; i++){
for (var j = 0; j < absentees[i].length; j++){
arrayOfObjects[i]._id = absentees[i][j]._id
arrayOfObjects[i].names = absentees[i][j].firstName + " " + absentees[i][j].lastName
}
}
Running the code above will return
UnhandledPromiseRejectionWarning: TypeError: Cannot set property '_id' of undefined at line
which basically points to the line
arrayOfObjects[i]._id
But it will assign the value without any problem, if I write
arrayOfObjects[0]._id
instead of
arrayOfObjects[i]._id
My assumption was let arrayOfObjects = [{ _id:0, names:'' }]; would create an array of objects, so I would be able to access/set its values with
arrayOfObjects[i]._id
but it seems let arrayOfObjects = [{ _id:0, names:'' }]; here will only create a single array of object. So currently for me to pass more values, I will need to declare it something like
let arrayOfObjects = [{ _id:0, names:'' },{ _id:0, names:'' },{ _id:0, names:'' },{ _id:0, names:'' },{ _id:0, names:'' }];
But of course, this isn't feasible because I don't know what should my array size be. So actually how should I declare the array of objects that can contain dynamic size of objects?

You could simply check if the element already exists in the array and if it doesn't, create it.
let arrayOfObjects = [];
for (var i = 0; i < kelasList.length; i++) {
for (var j = 0; j < absentees[i].length; j++) {
if (!arrayOfObjects[i]) {
arrayOfObjects[i] = {
_id: 0,
names: ''
};
}
arrayOfObjects[i]._id = absentees[i][j]._id
arrayOfObjects[i].names = absentees[i][j].firstName + " " + absentees[i][j].lastName
}
}

By setting arrayOfObjects[i]._id, you are setting the attribute _id on an already existing object arrayOfObjects[i]. But for i >= 1, this object doesn't exist.
Instead, you need to set the whole object in the loop:
for (var i = 0; i < kelasList.length; i++){
for (var j = 0; j < absentees[i].length; j++){
arrayOfObjects[i] = {
_id: absentees[i][j]._id,
names: absentees[i][j].firstName + " " + absentees[i][j].lastName}
}
}
This will work just fine, but will replace the whole object at the index i. If instead you want to set the _id and names attribute of an object that could already be existing with different keys (say, address), you would check if the object exists:
for (var i = 0; i < kelasList.length; i++){
for (var j = 0; j < absentees[i].length; j++){
let id = absentees[i][j]._id;
let names = arrayOfObjects[i].names = absentees[i][j].firstName + " " + absentees[i][j].lastName;
if(arrayOfObjets[i]){
arrayOfObjects[i]._id = id;
arrayOfObjects[i].names = names;
}else{
arrayOfObjects[i] = {
_id: absentees[i][j]._id,
names: absentees[i][j].firstName + " " + absentees[i][j].lastName
}
}
}
}
There is one last problem with your code. You are iterating over i, then over j, and are setting arrayOfObjects[i]. This means that your double loop is basically the same thing as:
for (var i = 0; i < kelasList.length; i++){
let lastAbsentee = absentees[i][absentees[i].length-1];
arrayOfObjects[i]._id = lastAbsentee._id
arrayOfObjects[i].names = lastAbsentee.firstName + " " + lastAbsentee.lastName
}
Are you sure that is what you want to do? Somehow I would be doubting it.

Your arrayOfObjects has a size of 1 when you initialize it, but then you look into arrayOfObjects[i] after i === 0 which cause the undefined behavior since there are no items there (yet).
In your for loop, you're also overwriting on arrayOfObjects[i] multiple times (j times to for precision).
Try using this
let arrayOfObjects = [];
for (var i = 0; i < absentees.length; i++) {
for (var j = 0; j < absentees[i].length; j++) {
arrayOfObjects.push({
_id: absentees[i][[j]._id,
names: absentees[i][j].firstName + " " + absentees[i][j].lastName
});
}
}
NOTE: The iteration logic may be subject to change since I didn't really get your intention from your code. The "adding items to the arrray" part is good though.

Related

Recursively get documents in Azure Cosmos Stored Procedure

I'm trying to generate a content tree for a simple wiki. Each page has a Children property that stores the id of other wiki pages. I'm trying to write a SPROC that gets all of the documents, then iterate over each page's Children property and replace each item with an actual wiki page document. I'm able to get the first set of documents, but the wikiChildQuery returns undefined and I'm not quite sure why.
I've been able to get a document with the query alone but for some reason, it doesn't work within the SPROC. Is there something I'm missing here?
function GetWikiMetadata(prefix) {
var context = getContext();
var collection = context.getCollection();
var metadataQuery = 'SELECT {\"ExternalId\": p.ExternalId, \"Title\": p.Title, \"Slug\": p.Slug, \"Children\": p.Children} FROM Pages p'
var metadata = collection.queryDocuments(collection.getSelfLink(), metadataQuery, {}, function (err, documents, options) {
if (err) throw new Error('Error: ', + err.message);
if (!documents || !documents.length) {
throw new Error('Unable to find any documents');
} else {
var response = getContext().getResponse();
for (var i = 0; i < documents.length; i++) {
var children = documents[i]['$1'].Children;
if (children.length) {
for (var j = 0; j < children.length; j++) {
var child = children[j];
children[j] = GetWikiChildren(child);
}
}
}
response.setBody(documents);
}
});
if (!metadata) throw new Error('Unable to get metadata from the server');
function GetWikiChildren(child) {
var wikiChildQuery = metadataQuery + ' WHERE p.ExternalId = \"' + child + '\"';
var wikiChild = collection.queryDocuments(collection.getSelfLink(), wikiChildQuery, {}, function(err, document, options) {
if (err) throw new Error('Error: ', + err.message);
if (!document) {
throw new Error('Unable to find child Wiki');
} else {
var children = document.Children;
if (children) {
for (var k = 0; k < children.length; k++) {
var child = children[k];
children[k] = GetWikiChildren(child);
}
} else {
return document;
}
}
if (!wikChild) throw new Error('Unable to get child Wiki details');
});
}
}
1.I'm able to get the first set of documents, but the wikiChildQuery
returns undefined and I'm not quite sure why.
Firstly, here should be corrected. In the first loop, you get children array with documents[i]['$1'].Children, however , in the GetWikiChildren function you want to get children array with document.Children? Surely,it is undefined. You need to use var children = document[0]['$1'].Children;
2.It seems that you missed the replaceDocument method.
You could refer to the snippet code of your metaDataQuery function:
for (var i = 0; i < documents.length; i++) {
var children = documents[i]['$1'].Children;
if (children.length) {
for (var j = 0; j < children.length; j++) {
var child = children[j];
children[j] = GetWikiChildren(child);
}
}
documents[i]['$1'].Children = children;
collection.replaceDocument(doc._self,doc,function(err) {
if (err) throw err;
});
}
3.Partial update is not supported by Cosmos db SQL Api so far but it is hitting the road.
So, if your sql doesn't cover your whole columns,it can't be done for your replace purpose.(feedback) It is important to note that the columns which is not mentioned in the replace object would be devoured while using replaceDocument method.

Column length is returning zero when I pass the value i in the xpath using for loop in nodejs webdriver

This is the code to iterate through a dynamic table.
driver.findElements(By.xpath(`//*[#id="slide-wrapper"]/div/ui-view/search-activities/div/div[2]/table/tbody/tr`)).then(function(rows) {
console.log("NoofRowsinthetable" + rows.length);
var identifyvalue = "6/16/17 12:41 PM"
var datacount = 0;
for (var i = 1; i <= rows.length; i++) {
driver.findElements(By.xpath(`//*[#id="slide-wrapper"]/div/ui-view/search-activities/div/div[2]/table/tbody/tr[i]/td`)).then(function (cells) {
console.log("NoofColumnsinthetable" + cells.length);
for (var j = 1; j <= cells.length; j++) {
driver.findElement(By.xpath(`//*[#id="slide-wrapper"]/div/ui-view/search-activities/div/div[2]/table/tbody/tr[i]/td[j]`)).getText().then(function (cell_text) {
console.log(cell_text);
if (identifyvalue === cell_text) {
datacount = datacount + 1;
console.log("Data count" + datacount);
}
})
First Forloop:
Cells.length returns zero even if there are 9 columns.
IF I AM PASSING THE VALUE OF I AS STRING(tr["+i+"]) THEN IT IS RETURNING THE VALUE OF COLUMN.LENGTH OF FIRST ROW ONLY AND IF I DO NOT PASS THE VALUE OF ‘I’ AS STRING THEN IT IS RETURING THE COLUMN.LENGTH AS ZERO.
Second For loop:
IF I AM PASSING THE VALUE OF I AND J AS STRING(tr["+i+"]/td["+j+"]), THEN IT IS RETURNING THE VALUE OF FIRST COLUMN ONLY
The problem is with single quote. you are starting the string with single quote and it should be ended with single. try the following,
driver.findElements(By.xpath(`//*[#id="slide-wrapper"]/div/ui-view/search-activities/div/div[2]/table/tbody/tr`)).then(function(rows) {
console.log("NoofRowsinthetable" + rows.length);
var identifyvalue = "6/16/17 12:41 PM"
var datacount = 0;
for (var i = 1; i <= rows.length; i++) {
driver.findElements(By.xpath(`//*[#id="slide-wrapper"]/div/ui-view/search-activities/div/div[2]/table/tbody/tr[`+i+`]/td`)).then(function (cells) {
console.log("NoofColumnsinthetable" + cells.length);
for (var j = 1; j <= cells.length; j++) {
driver.findElement(By.xpath(`//*[#id="slide-wrapper"]/div/ui-view/search-activities/div/div[2]/table/tbody/tr[`+i+`]/td[`+j+`]`)).getText().then(function (cell_text) {
console.log(cell_text);
if (identifyvalue === cell_text) {
datacount = datacount + 1;
console.log("Data count" + datacount);
}
})

Is a for loops scope unique

I ran into this while performing some technical debt duties. What is the scope of the variable foo ? Is it really "already defined"?
function fn(){
for (var i = 0; i < m.length; i++) {
if(condition)
for (var j = 0; j < m.length; j++) {
var foo = "bar";
}
else
var foo = "fubar"
}
}
UPDATE: The question is about the scope of the variables defined within a conditional block. Since this isn't nested in a function/closure there is no unique scope.
Here is a snippet to illustrate:
var x = "foo",
a = [];
for(var i=0;i<10;i++){
var x = {value:1+i};
a.push(x)
}
document.write("<pre>" +
x.value + "\n" +
JSON.stringify(a,null," ") +
"</pre>"
);
JavaScript only has function scope, not block scope. So your variable foo exists at function level and both assignments refer to same instance.
var m = [ 1, 2, 3 ];
var x = fn(m, true);
WScript.Echo( x );
var x = fn(m, false);
WScript.Echo( x );
function fn(m, condition){
for (var i = 0; i < m.length; i++) {
if(condition)
for (var j = 0; j < m.length; j++) {
var foo = "bar";
}
else
var foo = "fubar"
}
return foo;
}
A variable in JavaScript declared inside an if or for is accessible outside the if or for after the line of code that has the declaration has been run. For instance:
function DoThing() {
for (var i = 0; i < 1; ++i)
var x = 0;
return x;
}
DoThing(); // returns 0;
In the example you provided the variable is declared after reaching the body of the for loop given condition is true, or in the body of the else statement. Since those conditions are mutually exclusive it is never re-declared by that condition alone. The variable will be re-declared by the loop it is within however.
That said the code isn't very nice to read and I would recommend refactoring it to not have a for loop nested within an if statement, and not declare a new variable within both an if and an else, let alone within the body of a for loop.

SP.NavigationNode.get_isVisible() broken?

I need to read the "Top Nav", the "Children Nodes" and check if each node is visible.
I am using JSOM to accomplish this. Everything is working fine except for the get_isVisible() function. It always returns true. MSDN: http://msdn.microsoft.com/en-us/library/office/jj246297.aspx
I am on a publishing site in 2013 and I know some of the items are hidden. (My web and context are defined outside of this snippet)
var visParents = [], visChildren = [];
var topNodes = web.get_navigation().get_topNavigationBar();
context.load(topNodes);
context.executeQueryAsync(onQuerySucceeded, onQueryFailed)
function onQuerySucceeded() {
var nodeInfo = '';
var nodeEnumerator = topNodes.getEnumerator();
while (nodeEnumerator.moveNext()) {
var node = nodeEnumerator.get_current();
nodeInfo += node.get_title() + '\n';
if (node.get_isVisible())
visParents.push(node);
}
console.log("Current nodes: \n\n" + nodeInfo);
console.log("Visible Parents", visParents)
}
function onQueryFailed(sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
It is a known issue, it seems that SP.NavigationNode.isVisible property does not correspond to the property that indicates whether navigation node is hidden or shown.
Please refer "Hidden" property of SPNavigationNode for a details
The following function demonstrates how to retrieve hidden node Urls:
function getGlobalNavigationExcludedUrls(Success,Error)
{
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var subwebs = web.get_webs();
var pagesList = web.get_lists().getByTitle("Pages");
var pageItems = pagesList.getItems(SP.CamlQuery.createAllItemsQuery());
var allProperties = web.get_allProperties();
context.load(web);
context.load(subwebs);
context.load(allProperties);
context.load(pageItems);
context.executeQueryAsync(
function() {
var excludedIds = allProperties.get_item('__GlobalNavigationExcludes').split(';');
var exludedUrls = [];
for (var i = 0; i < excludedIds.length - 1; i++ )
{
for (var j = 0; j < subwebs.get_count(); j++ )
{
var subweb = subwebs.getItemAtIndex(j);
if(subweb.get_id().toString() == excludedIds[i]){
exludedUrls.push(subweb.get_serverRelativeUrl());
break;
}
}
for (var j = 0; j < pageItems.get_count(); j++ )
{
var pageItem = pageItems.getItemAtIndex(j);
if(pageItem.get_item('UniqueId').toString() == excludedIds[i]){
exludedUrls.push(web.get_serverRelativeUrl() + pageItem.get_item('FileRef'));
break;
}
}
}
Success(exludedUrls);
},
Error
);
}
//Usage: print excluded nodes Urls
getGlobalNavigationExcludedUrls(function(excludedNodeUrls){
for (var j = 0; j < excludedNodeUrls.length; j++ )
{
console.log(excludedNodeUrls[j]);
}
},
function(sender,args){
console.log(args.get_message());
});

How can I call asynchronous functions in a nested for loop without running out of memory?

I have code like this:
for (var i in data) {
for (var j in data[i]) {
for (var k in data[i][j]) {
db.data.insert({i:i, j:j, k:k}, emptyCallback);
}
}
}
but I run out of memory because it's queuing up all the inserts. How can I make the for loop pause until the insert is complete?
I've tried pushing all the records to an array to insert later, but then the array gets too big and again I run out of memory.
you need to iterate to next key each time callback is called, something like this:
function get_first_keys(data, keys)
{
var ki = Object.keys(data);
for (var i = keys[0]; i < ki.length; ++i)
{
var kj = Object.keys(data[ki[i]]);
for (var j = keys[1]; j < kj.length; ++j)
{
var kk = Object.keys(data[ki[i]][kj[j]]);
for (var k = keys[2]; k < kk.length; ++k)
{
return [i, j, k];
}
}
}
}
function get_next_keys(data, keys)
{
var i = keys[0];
var j = keys[1];
var k = keys[2];
var ki = Object.keys(data);
var kj = Object.keys(data[ki[i]]);
var kk = Object.keys(data[ki[i]][kj[j]]);
if (k + 1 < kk.length)
return [i, j, k + 1];
if (j + 1 < kj.length)
return get_first_keys(data, [i, j+1, 0]);
if (i + 1 < ki.length)
return get_first_keys(data, [i+1, 0, 0]);
return;
}
var current_keys = get_first_keys(data, [0, 0, 0]);
function insert()
{
if (!current_keys)
return;
key = {};
key.i = Object.keys(data)[current_keys[0]];
key.j = Object.keys(data[key.i])[current_keys[1]];
key.k = Object.keys(data[key.j])[current_keys[2]];
db.data.insert(key, insert);
current_keys = get_next_keys(data, current_keys);
}
insert();
The simple answer is to do it recursively: do the next insert in the callback rather than passing an empty callback.

Resources