nested for loop in node js with asynchronous - javascript

I have userCurrentContainer = [[],[{"1":"xyz"},{"2":"abc"}],[{"1":"xyz"}],[]]
I to want to group by key so the output will look like: { 1: xyzxyz, 2: abc}
I am using async Library.
I understood grouping logic ..
but my problem is when everything done then i will callback..
but it is before calling..
function grupbyContainerId(userCurrentContainer,callback){
var track=0;
var temp=new Array();
async.eachSeries(userCurrentContainer, function (key,callback1) {
track=track+1
if(key.length>0){
var innertrack=0;
async.eachSeries(key, function (key1,callback2) {
innertrack=innertrack+1
//logic here
if(track>=userCurrentContainer.length&&innertrack,key.length>=key.length){
console.log("if calledd ---------->");
callback(null,temp)
}
callback2();
})
}
callback1();
});
}

Please, reffer to http://caolan.github.io/async/docs.html#eachSeries
It should have 3 params. Try something like that:
function doSomeAction() {
var someNestedArray = [[],[{key:'value1'}],[],[{key:'value2'}]];
async.eachSeries(someNestedArray, function (item, next) {
async.eachSeries(item, function (deepObj, cb) {
console.log(deepObj)
cb()
}, function (err, result) {
next();
})
}, function (err, res) {
console.log('AllWorkDone');
})}

Judging by the basic input/output example you give, I think using reduce might be a better fit for you. E.g.
const input = [[],[{"1":"xyz"},{"2":"abc"}],[{"1":"xyz"}],[]];
const output = input.reduce(function (acc, value) {
for (let val of value) {
acc[Object.keys(val)] = val[Object.keys(val)] + (acc[Object.keys(val)] || '');
}
return acc;
}, {});
console.log(output);

Related

Array are adding only in 0th index

I am writing a function in which i need to add the elements into an array.I am getting the data from my database.When add the element into it all the elements are only being added to the Array[0] of the rowData.
private createRowData() {
var rowData:any[] = [];
this.wakanda.catalog.then(ds => {
ds.Group.query({select : 'groupName'}).then(op => {
for(let entity of op['entities']){
rowData.push(
{row : entity.groupName});
}
});
});
return rowData;
}
My output is like this
I need something like this
How do i solve it
Thanks in advance
In the above function you are using DB call which is async and then you are sending the response without waiting for the result.
So, In this case, you will get the rowData.length 0.
send the result after the callback response.
try this:
private createRowData() {
return new Promise((resolve, reject) => {
var rowData: any[] = [];
this.wakanda.catalog.then(ds => {
ds.Group.query({ select: 'groupName' }).then(op => {
for (let entity of op['entities']) {
rowData.push({ row: entity.groupName });
}
resolve(rowData); // Send result from here
});
}).catch(reject);
})
}

Return promises in order

I'm facing an issue to return promises using $q#all method.
I want to make promises dependent on each other, i.e.:
If I set obj1, obj2 and obj3 I want to get them in the same order.
How can I achieve this?
Factory:
mainFactory.$inject = ['$http', '$q'];
function mainFactory($http, $q) {
var mainFactory = {
getPromises: getPromises
};
return mainFactory;
function getPromises(id) {
promises = {
'obj1': $http.get('http1'),
'obj2': $http.get('http2'),
'obj3': $http.get('http3'),
'obj4': $http.get('http4', { params: { 'id': id } }),
'obj5': $http.get('http5'),
'obj6': $http.get('http6', { params: { 'id': id } })
};
return $q.all(promises);
}
}
Controller:
MainCtrl.$inject = ['mainFactory'];
function MainCtrl(mainFactory) {
var vm = this;
mainFactory.getPromises(id)
.then(getResponse)
.catch(getError);
function getResponse(response) {
var keys = Object.keys(response), i = keys.length;
while (i--) {
var key = keys[i];
console.log(key); // I want all the keys in order, i.e. => obj1, obj2.. and so on
var value = response[key].data;
switch(key) {
...
}
}
}
function getError(error) {
console.log(error);
}
}
EDIT:
I tried this way also:
var promises = {};
return $http.get('/admin/http1.json').then(function (value) {
promises['obj1'] = value;
})
.then(function (result) {
return $http.get('/admin/http2.json').then(function (value) {
promises['obj2'] = value;
});
}).then(function (result) {
return $http.get('/admin/http3.json').then(function (value) {
promises['obj3'] = value;
});
});
return $q.all(promises);
Using $q.all will resolve each promise in no particular order. If you want them to execute after each promise has been resolve, use promise chaining.
function getPromises(id) {
var getObjA = function () {
return $http.get('http1');
};
var getObjB = function () {
return $http.get('http2');
};
var getObjC = function () {
return $http.get('http3');
};
var getObjD = function () {
return $http.get('http4', { params: { 'id': id } });
};
var getObjE = function () {
return $http.get('http5');
};
var getObjF = function () {
return $http.get('http5', { params: { 'id': id } });
};
return getObjA()
.then(getObjB)
.then(getObjC)
.then(getObjD)
.then(getObjE)
.then(getObjF);
}
Edit: as an additional info, you can catch any error in any of those promise by placing a catch statement here
getPromises("id")
.then(<success callback here>)
.catch(<error callback that will catch error on any of the promises>);
Meaning, once a promise fails, all the succeeding promises below wouldn't be executed and will be caught by your catch statement
Edit 2
Mistake, I just copied you code above without realizing it was an object. LOL.
promises = [
$http.get('http1'),
$http.get('http2'),
$http.get('http3'),
$http.get('http4', { params: { 'id': id } }),
$http.get('http5'),
$http.get('http6', { params: { 'id': id } })
]
Edit 1
Sorry I didn't notice the comments Jared Smith is correct.
Object keys are inherently unordered. Use an array instead.
Edit 0
Object keys wont be ordered. Use array on declaring your promises.
promises = [
$http.get('http1'),
$http.get('http2'),
$http.get('http3'),
$http.get('http4', { params: { 'id': id } }),
$http.get('http5'),
$http.get('http6', { params: { 'id': id } })
]
$q.all(promises)
.then(functions(resolves){
// resolves here is an array
}).catch(function(err){
// throw err
});

How do I send value from returned from async method to another async method in javascript?

I have tasks defined as array of objects. getHbaseAllCountries returns all the country codes as an array which I’m trying to store in the variable selectedCountries. I pass this var selectedCountries [] to getComponentsGraphData to get the data. But when I console.log(selectedCountries) it gives me empty array [] though I have added a callback.
var tasks = [{
func: 'getHbaseAllCountries',
options: options
}];
var results = [];
var selectedCountries= [];
async.forEach(tasks, function(value, callback) {
if(value["func"] === 'getHbaseAllCountries') {
cmodel.getHbaseAllCountries( value["options"],function(err, values) {
if (err) {
v.send(err);
return;
}
for(var index=0; index< values.length; index++){
selectedCountries.push(values[index].key);
}
console.log(selectedCountries);// prints the desired results
callback(err,values);
});
}
console.log(selectedCountries);// prints []
cmodel.getComponentsGraphData(type, selectedCountries, value["country"], value["model"], function(err, data) {
//console.log("Data for: " + JSON.stringify(value));
results.push({
_id: value["key"],
data: data
});
callback(err, data);
});
}, function(err) {
if (err) {
v.send(err);
return;
}
v.send(results);
});
You call getComponentsGraphData but you don't know if your request getHbaseAllCountries as ended. You should move your call of getComponentsGraphData as suggested in the comments. Also, the use of chained promises would be better.
if(value["func"] === 'getHbaseAllCountries') {
cmodel.getHbaseAllCountries( value["options"],function(err, values) {
[...]
cmodel.getComponentsGraphData....
}
}else{
cmodel.getComponentsGraphData....
}

Executing a function in a loop in javascript asynchronously- where to place defer.resolve?

I come from java/python background and new to javascript. I need to create a product list with the description of its children as well included in a jsonarray.
parent_list:
[{ children: [ 100714813, 100712694 ],
sp: '89.10',
weight: '1 ltr',
pack_type: 'Carton',
brand: 'Real',
p_desc: 'Fruit Power Juice - Orange' }]
Now for every parent I need to again iteratively fetch the children details by connecting to the database and finally have the result consolidated in a single jsonarray. But when I execute the below code, the control doesn't wait for fetching the children data( which makes sense as its being called asynchronously!), the result I get is a jsonarray that contains data only for the parents that have no children.
exports.productDetailsQuery = function(options) {
var AEROSPIKE_NAMESPACE = '';
var AEROSPIKE_SET = 'products';
var PD_KEY_VERSION_NUMBER = '1';
var defer = sails.Q.defer();
var results = options.results;
var parent_list = [];
var finalData = [];
var productKeys = results.map(
function(x){
return {
ns: AEROSPIKE_NAMESPACE,
set: AEROSPIKE_SET,
key: "pd.v" + PD_KEY_VERSION_NUMBER + '.' + 'c' + options.city_id + '.' + x.sku.toString()
}
}
);
var status = require('aerospike').status;
var breakException = {};
// Read the batch of products.
sails.aerospike.batchGet(productKeys, function (err, results) {
if (err.code === status.AEROSPIKE_OK) {
for (var i = 0; i < results.length; i++) {
switch (results[i].status) {
case status.AEROSPIKE_OK:
parent_list.push(results[i].record);
break;
case status.AEROSPIKE_ERR_RECORD_NOT_FOUND:
console.log("NOT_FOUND - ", results[i].keys);
break;
default:
console.log("ERR - %d - ", results[i].status, results[i].keys);
}
}
parent_list.forEach(function(parent){
var children = parent['children'];
console.log(children)
if(children){
var childKeys = children.map(function(child){
return {
ns: AEROSPIKE_NAMESPACE,
set: AEROSPIKE_SET,
key: "pd.v" + PD_KEY_VERSION_NUMBER + '.' + 'c' + options.city_id + '.' + child.toString()
}
});
sails.aerospike.batchGet(childKeys, function(err, childData){
if(err.code === status.AEROSPIKE_OK){
console.log('this called')
var entry = {};
entry['primary_prod'] = parent;
entry['variants'] = childData;
finalData.push(entry);
}
});
}
else{
var entry = {};
entry['primary_prod'] = parent;
finalData.push(entry);
}
});
defer.resolve(finalData);
} else {
defer.reject(err);
}
});
return defer.promise;
}
I need finalData to be like:
[{"primary_prod":{ children: [ 100714813, 100712694 ],
sp: '89.10',
weight: '1 ltr',
pack_type: 'Carton',
brand: 'Real',
p_desc: 'Fruit Power Juice - Orange' },
"variants":[{child_data},{child_data}]}, ...........]
Would really appreciate any help as to how to make it work.Is there a specific pattern to handle such cases?
Thanks!
What you have written is along the right lines but only the outer batchGet() is promisified. Because there's no attempt to promisify the inner batchGet(), it doesn't contribute to the finally returned promise.
Your overall pattern might be something like this ...
exports.productDetailsQuery = function(options) {
return sails.aerospike.batchGetAsync(...).then(results) {
var promises = results.filter(function(res) {
// Filter out any results that are not `AEROSPIKE_OK`
...
}).map(function(parent) {
// Map the filtered results to an array of promises
return sails.aerospike.batchGetAsync(...).then(function(childData) {
...
});
});
// Aggregate the array of promises into a single promise that will resolve when all the individual promises resolve, or will reject if any one of the individual promises rejects.
return sails.Q.all(promises);
});
}
... where batchGetAsync() is a promisified version of batchGet().
The fully fleshed-out the code will be longer but can be kept reasonably concise, and readable, by first defining a couple of utility functions. You might end up with something like this :
// utility function for making a "key" object
function makeKey(obj) {
return {
ns: '', //AEROSPIKE_NAMESPACE
set: 'products', //AEROSPIKE_SET
key: 'pd.v1.c' + options.city_id + '.' + obj.toString()
}
}
// promisified version of batchGet()
function batchGetAsync(obj) {
var defer = sails.Q.defer();
batchGet(obj, function(err, results) {
if(err.code === status.AEROSPIKE_OK) {
defer.resolve(results);
} else {
defer.reject(err);
}
});
return defer.promise;
}
var status = require('aerospike').status;
// Main routine
exports.productDetailsQuery = function(options) {
return batchGetAsync(options.results.map(makeKey)).then(results) {
var promises = results.filter(function(res) {
if (res.status === status.AEROSPIKE_OK) {
return true;
} else if(status.AEROSPIKE_ERR_RECORD_NOT_FOUND) {
console.log("NOT_FOUND - ", res.keys);
} else {
console.log("ERR - %d - ", res.status, res.keys);
}
return false;
}).map(function(parent) {
var entry = { 'primary_prod': parent },
children = parent['children'];
if(children) {
return batchGetAsync(children.map(makeKey)).then(function(childData) {
entry.variants = childData;
return entry;
});
} else {
return entry;
}
});
return sails.Q.all(promises);
});
}
With the new ES6 plus async stuff and babel its simpler. You can npm i -g babel npm i babel-runtime then compile and run the following with babel test.js --optional runtime --stage 2 | node:
import {inspect} from 'util';
let testData = [
{ id: 0, childIds: [1,2]},
{ id: 1, childIds:[] },
{ id: 2, childIds:[] }
];
function dbGet(ids) {
return new Promise( r=> {
r(ids.map((id) => { return testData[id];}));
});
}
async function getChildren(par) {
let children = await dbGet(par.childIds);
par.children = children;
}
async function getAll(parentIds) {
let parents = await dbGet(parentIds);
for (let p of parents) {
await getChildren(p);
}
return parents;
}
async function test() {
var results = await getAll([0]);
console.log(inspect(results,{depth:3}));
}
test().then(f=>{}).catch( e=> {console.log('e',e)});

Creating json in specific format using javascript

I have a complex javascript code which when simplified is as below..
function getjson1() {
return {
'json1': {
id: 'jsonid1'
}
};
}
function getjson2() {
return {
'json2': {
id: 'jsonid2'
}
};
}
myjson = [];
myjson.push(getjson1());
myjson.push(getjson2());
function finaljson() {
return {
'json': myjson
};
}
console.log(JSON.stringify(finaljson()));
Now the result of this code is
{"json":[{"json1":{"id":"jsonid1"}},{"json2":{"id":"jsonid2"}}]}
Now this code I need to change such that I can get rid of the array and can traverse the json object like.. json.json1.id, etc..
One example could be as below..
{"json":{"json1":{"id":"jsonid1"},"json2":{"id":"jsonid2"}}}
Any help is sincerely appreciated.
Thanks
Well if you don't want an array, don't use one. First, a jQuery-based solution:
myjson = {};
myjson = $.extend(myjson, getjson1());
myjson = $.extend(myjson, getjson2());
In native JavaScript, you can use the following function:
function extend (target, source) {
Object.keys(source).map(function (prop) {
target[prop] = source[prop];
});
return target;
};
This way, the first code becomes this:
myjson = {};
myjson = extend(myjson, getjson1());
myjson = extend(myjson, getjson2());
You are pushing it to an array so you are getting an array.
use this simple add function to push it in an object in the format you want.
First key in the function returns will be the key in the end object.
function getjson1() {
return {
'json1': {
id: 'jsonid1'
}
};
}
function getjson2() {
return {
'json2': {
id: 'jsonid2'
}
};
}
function add(obj, toadd) {
for(var key in toadd) {
if(toadd.hasOwnProperty(key)) {
obj[key] = toadd[key];
break;
}
}
return obj;
}
myjson = {};
add(myjson,getjson1());
add(myjson,getjson2());
function finaljson() {
return {
'json': myjson
};
}
console.log(JSON.stringify(finaljson()));

Categories

Resources