I'm having difficulties sending a populated array after two nested loops has completed iterating. I'm using the Async npm library and trying to use the async.forEach completion callback to send the entire array. The inner array iterates over 5 objects, which constitutes a "course" - afterwhich the course object is saved to a person object and there's multiple course objects per person object.
var self = this,
person = [],
course = [];
async.forEach(elements.value, function (element, callback1) {
self.elementIdElements(element.ELEMENT, 'td').then(function (rows) {
async.forEach(rows.value, function (cell, callback2) {
self.elementIdText(cell.ELEMENT).then(function (res) {
course.push(res.value);
callback2();
});
}, callback1);
person.push(course);
course = [];
});
}, function (err) {
res.send('grades: ' + JSON.stringify(person));
});
However, it calls the sendResponse in the middle of iterating through the arrays and I simply cannot phantom why this is happening, it should call it after iterating through the whole thing. The scope and asynchronous calls is confusing
Thanks in advance,
Chris
UPDATE - SOLUTION
I finally figured out the scope of the callbacks with the following code:
var self = this,
person = [],
course = [];
async.forEach(elements.value, function (element, callback1) {
self.elementIdElements(element.ELEMENT, 'td').then(function (rows) {
async.forEach(rows.value, function (cell, callback2) {
self.elementIdText(cell.ELEMENT).then(function (res) {
course.push(res.value);
callback2();
});
}, function (err) {
person.push(course);
course = [];
callback1();
});
});
}, function (err) {
res.send('grades: ' + JSON.stringify(person));
});
You must nest your callbacks inside each other. With the async library, the last argument of many of the calls is the "final callback", which lets you know when everything is done.
You're outer async.forEach looks good, where the final callback of sendResponse is the final argument.
Change the inner async.forEach so callback is the final argument. async.forEach(rows.value, getCourseData, callback)
I might also suggest more descriptive callback names so you can easily keep track ("innerCallback" "outerCallbacl" or "rowsCallbacl" ... whatever you want)
Related
I'm still brand new to Angular and Javascript, looked around for a solution to this but having a hard time finding one so please bear with me here. I have an Angular function that looks like this:
post: function(url, params) {
var someObject = {
'key': ['value1', 'value2']
}
for (var paramKey in params) {
angular.forEach(someObject, function (values, key) {
if (paramKey.indexOf(key) !== -1) {
debugger; // params is still defined here
angular.forEach(values, function (innerValue) {
if (paramKey.indexOf(innerValue) !== -1) {
debugger; // params is no longer defined
}
}, params)
}
}, params)
}
I want to iterate through some keys and values and based on what I find in the inner loop, I want to manipulate the params object before sending it to the API.
Looking at the forEach documentation, I can use the context argument to pass the object inside the forEach and keep it in scope. However, I'm using a nested loop, and for some reason it seems I can't keep passing params via context through into the nested loop. It just becomes undefined the second time.
How can I access and manipulate the params object in the inner loop?
Nesting loops is always best to be avoided, so I kind of thought, why do you even need to itterate if you are just checking the key of the object and then check if the key matches any of array strings. You can do the same thing like this:
post: (url, params) => {
var someObject = {
'key': ['value1', 'value2']
}
Object.values(params).forEach((paramKey) => {
if(someObject[paramKey]) {
if(someObject[paramKey].includes(paramKey)) {
}
}
})
}
I have an array of players, each player is an object that has a number of properties, one is "goals".
var players = [
{
"id":"4634",
"name":"A. Turan",
"number":"0",
"age":"28",
"position":"M",
"goals":"1"
},
{
"id":"155410",
"name":"H. Çalhano?lu",
"number":"0",
"age":"21",
"position":"A",
"goals":"0"
},
{
"id":"4788",
"name":"B. Y?lmaz",
"number":"0",
"age":"30",
"position":"A",
"goals":"2",
}
]
I've written a function to cycle through the array and push every element that has more than '0' goals to an array, topScorers. Like so:
$scope.topScorerSearch = function() {
var topScorers = [];
$scope.teamDetails.squad.forEach(function(o) {
if (o.goals > 0) {
topScorers.push(o)
}
});
return topScorers;
}
With the function called as {{topScorerSearch()}}.
This returns only players who have scored. Perfect.
However, I want to run this on other properties, which will result in a lot of repetitious code. How can I make this a general purpose function that can be executed on different properties?
I tried including the 'prop' parameter, but it didn't work:
$scope.topScorerSearch = function(prop) {
var topScorers = [];
$scope.teamDetails.squad.forEach(function(o) {
if (o.prop > 0) {
topScorers.push(o)
}
});
return topScorers;
}
...and called the function like this:
{{topScorerSearch(goals)}}
Why doesn't this work? Where am I going wrong?
I believe the issue is that prop will not resolve to goals because goals is being treated as a variable with a null or undefined value, making prop null or undefined.
If you use the alternative way of accessing object properties object["property"] and use the function {{topScorers("goals")}} it should work out.
I need help with array asynchronous iterate functionality. I working with node-opcua library in nodejs. There is function session.browse(nodeId, result)
Right now code looks like:
NodesTree = {
"NodesTree":{
"name":"SYM:",
"subf":[]
}
};
the_session.browse("ns=1;s=SYM:", function(err, browse_result){
if(!err) {
var buf = [];
browse_result[0].references.forEach(function(reference) {
if (reference1.browseName.namespaceIndex > 1) {
buf.push(reference);
}
});
NodesTree.subf = buf;
}
});
In result I get references of SYM: folder example:
[{"referenceTypeId":"ns=0;i=35","isForward":true,"nodeId":"ns=6;s=S71500ET200MP station_1","browseName":{"namespaceIndex":6,"name":"S71500ET200MP station_1"},"displayName":{"text":"S71500ET200MP station_1","locale":"en"},"nodeClass":"Object","typeDefinition":"ns=0;i=61"}]
I have Nodes structure in opc like this:
->SYM:
-->PLC
--->PLC_name
---->global_tag <variable>
---->global_tag1 <variable>
---->block
------>blok_tag1 <variable>
------>block_tag2 <variable>
Task is make one complete JSON object as tree for further use.
Logic is that: for each element in the references array get nodeId value and browse for references of the element and assign as element.subf = reference.
Final result something like:
NodesTree = {
"NodesTree":{
"name":"SYM:",
"subf":[
{attributes of PCL structure got by **browse**() + subf:[{ attributes of PLC_name by browse(), subf:
[{....and here again attributes and subf] }, {if no subf just assign subf; [] }]
]
}
};
So need call session.browse() for each reference and all finally bind to one object.
I tried to use Async library each and map in series functions to solve all that, but get nothing wise in result. May be there some smart solution can be found by Stack overflow community. Please help.
I am not familiar with node-opcua but assume that session.browse() is also async. Then something like this might work?
var async = require('async');
async.map(buf,
function(reference, callback) {
session.browse(reference.nodeId, function (err, result) {
callback(err, result);
});
}, function(err, results) {
// results is now an array of all single results
NodesTree.subf = results;
});
I have a 'flat' array with 3 items:
[{"title":"welcome","file":"default.aspx","category":"finance"},
{"title":"test2","file":"test2.aspx","category":"finance"},
{"title":"test1","file":"test1.aspx","category":"housing"}]
The objective is to transform this into a nested observableArray with 2 items:
[{"category":"finance","content":[
{"title":"welcome","file":"default.aspx","category":"finance"},
{"title":"test2","file":"test2.aspx","category":"finance"}]},
{"category":"housing","content":[
{"title":"test1","file":"test1.aspx","category":"housing"}]}]
http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html helped me to extract unique categories in two steps:
self.getcategories = ko.computed(function () {
var categories = ko.utils.arrayMap(self.pages(), function (item) {
return item.category();
});
return categories.sort();
});
self.uniqueCategories = ko.dependentObservable(function() {
return ko.utils.arrayGetDistinctValues(self.self.getcategories()).sort();
});
//uniqueCategories: ["finance","housing"]
However I can't figure out how to create the nested array. I got as far as this:
self.createCategories = ko.computed(function () {
ko.utils.arrayForEach(self.uniqueCategories(), function (item) {
var content = getCategoryContent(item);
var c = new category(item, content);
self.Categories.push(c);
});
return true;
});
function getCategoryContent(whichcategory) {
return ko.utils.arrayFilter(self.pages(), function (page) {
return page.category() === whichcategory;
});
}
It results however in 5 category items (finance 4x, housing 1x) where I expect just 2.
Your computed function createCategories is probably being invoked more than once. e.g. if you are adding items to your pages() array one at a time, this will trigger the createCategories function each time you add an item.
Normally you would make the categories array and return it from a computed function. Currently you are adding to the Categories observable array without clearing it each time.
An easy fix would be to clear out the Categories array at the top of the createCategories function. This would leave you in the odd situation of having to call createCategories at least once to set up the dependencies, but after that it would work automatically when the data changed.
Another option would be create an array and return it from the createCategories functions, then you could just rename the function to Categories and not have an observable array. This would be the way I would normally use computed functions.
On more option would be to just do the work as normal JavaScript functions (rather than computed) and just call createCategories manually when you change the original array (e.g. when you get a result back from your server).
I have two collection "contents" and "units". In the content collection is a field "unitID" which refers to the unit-collection. In the meteor publish function I want to add the unit type name of all new created contents:
Meteor.publish("contents", function () {
var self = this;
var handle = Contents.find().observe({
changed: function(contentdoc, contentid) {
var UnitName = Units.findOne({_id: contentdoc.unittypeid }, {fields: {type: 1}});
self.set("contents", contentid, {'content.0.typename': UnitName});
self.flush();
}
});
}
This works but it creates a new attribut "content.0.UnitName" instead of inserting the attribute "UnitName" in the first element of the content array:
[
{
_id:"50bba3ca8f3d1db27f000021",
'content.0.UnitName':
{
_id:"509ff643f3a6690c9ca5ee59",
type:"Drawer small"
},
content:
[
{
unitID:"509ff643f3a6690c9ca5ee59",
name: 'Content1'
}
]
}
]
What I want is the following:
[
{
_id:"50bba3ca8f3d1db27f000021",
content:
[
{
unitID:"509ff643f3a6690c9ca5ee59",
name: 'Content1',
UnitName:
{
_id:"509ff643f3a6690c9ca5ee59",
type:"Drawer small"
}
}
]
}
]
What am I doing wrong?
this.set within Meteor.publish only works on the top-level properties of an object, meaning it doesn't support Mongo-style dotted attributes. You'll have to call set with the entire new value of the contents array.
Caveat: What I am about to say is going to change in a future release of Meteor. We're currently overhauling the custom publisher API to make it easier to use, but in a way that breaks back-compatibility.
That said...
It looks like what you're trying to do is build a server-side join into the published collection "contents". Here, for reference, is the current code (as of 0.5.2) that publishes a cursor (for when your publisher returns a cursor object):
Cursor.prototype._publishCursor = function (sub) {
var self = this;
var collection = self._cursorDescription.collectionName;
var observeHandle = self._observeUnordered({
added: function (obj) {
sub.set(collection, obj._id, obj);
sub.flush();
},
changed: function (obj, oldObj) {
var set = {};
_.each(obj, function (v, k) {
if (!_.isEqual(v, oldObj[k]))
set[k] = v;
});
sub.set(collection, obj._id, set);
var deadKeys = _.difference(_.keys(oldObj), _.keys(obj));
sub.unset(collection, obj._id, deadKeys);
sub.flush();
},
removed: function (oldObj) {
sub.unset(collection, oldObj._id, _.keys(oldObj));
sub.flush();
}
});
// _observeUnordered only returns after the initial added callbacks have run.
// mark subscription as completed.
sub.complete();
sub.flush();
// register stop callback (expects lambda w/ no args).
sub.onStop(function () {observeHandle.stop();});
};
To build a custom publisher that is joined with another table, modify the added callback to:
check if the added object has the key you want to join by
do a find in the other collection for that key
call set on your subscription with the new key and value you want to be published, before you call flush.
Note that the above is only sufficient if you know the key you want will always be in the other table, and that it never changes. If it might change, you'll have to set up an observe on the second table too, and re-set the key on the sub in the changed method there.