Can't get the summation in for loop - javascript

I have a angular service inside a for loop that returns an array of object. I want to get the summation of the value returned by that service but I got nothing in the end. My service works fine but my problem is I can't get the summation. Below is my code
controller
var TotalOBValueLand = 0;
for(var i = 0; i < $scope.selectedProp.length; i++){
AccountService.getTopAccountDetails($scope.selectedProp[i]["propId"]).then(function(msg){
TotalOBValueLand += parseInt(msg.data[0].OBValueLand);
//my return data here has no error.
});
}
console.log(TotalOBValueLand); //I got zero;

Use Promise.all and array#map to get an array of results, then use Array#reduce to sum them up
var TotalOBValueLand = 0;
Promise.all($scope.selectedProp.map(function(prop) {
return AccountService.getTopAccountDetails(prop).then(function(msg){
return parseInt(msg.data[0].OBValueLand);
});
})).then(function(results) {
TotalOBValueLand = results.reduce(function(a, b) {
return a + b;
});
console.log(TotalOBValueLand);
});
In response to the comments
var TotalOBValueLand = 0;
var TotalOBValueBuilding = 0;
Promise.all($scope.selectedProp.map(function(prop) {
return AccountService.getTopAccountDetails(prop).then(function(msg){
return parseInt(msg.data[0]);
});
})).then(function(results) {
TotalOBValueLand = results.reduce(function(a, b) {
return a.OBValueLand + b.OBValueLand;
});
TotalOBValueBuilding = results.reduce(function(a, b) {
return a.OBValueBuilding + b.OBValueBuilding ;
});
console.log(TotalOBValueLand, TotalOBValueBuilding);
});
and a little more generic
Promise.all($scope.selectedProp.map(function(prop) {
return AccountService.getTopAccountDetails(prop).then(function(msg){
return parseInt(msg.data[0]);
});
})).then(function(results) {
var totals = results.reduce(function(result, a) {
Object.keys(a).forEach(function(key) {
result[key] = (result[key] || 0) + a[key];
});
return result;
}, {});
console.log(totals.OBValueLand, totals.OBValueBuilding);
});

You cannot access console.log(TotalOBValueLand); outside the response since .getTopAccountDetails() is asynchronous, it will be always 0.
try to wrap it inside,
var TotalOBValueLand = 0;
for(var i = 0; i < $scope.selectedProp.length; i++){
AccountService.getTopAccountDetails($scope.selectedProp[i]["propId"]).then(function(msg){
TotalOBValueLand += parseInt(msg.data[0].OBValueLand);
console.log(TotalOBValueLand);
});
}

The problem is that you are mixing asynchronous and synchronous functions. This should demonstrate what is going on a little for you
https://jsfiddle.net/Austio/v7goqk4d/
AccountService = {
getTopAccountDetails: function() {
return new Promise((resolve) => resolve(1))
}
}
var TotalOBValueLand = 0;
for(var i = 0; i < 2; i++){
AccountService.getTopAccountDetails().then(function(x){
TotalOBValueLand += x;
console.log('incremented async', TotalOBValueLand)
});
}
console.log('sync', TotalOBValueLand);
setTimeout(() =>
console.log('timeout', TotalOBValueLand), 2000)
Solution using an array of promises that we resolve
var TotalOBValueLand = 0;
promises = []
for(var i = 0; i < 2; i++){
promise = AccountService
.getTopAccountDetails()
promises.push(promise)
}
console.log('before', TotalOBValueLand);
Promise
.all(promises)
.then(results => {
TotalOBValueLand = results.reduce((curr,acc) => curr + acc, 0);
console.log('done', TotalOBValueLand);
return TotalOBValueLand;
})
.catch(err => 'handle me')

Related

$.when apply for single request

I am trying to use $.when apply in my code. However, it seems that the format return is different for single and multiple request. How can i cater for it?? I am trying not to have another if else outside of it.
$.when.apply(null, apiRequestList).then(function () {
for (var i = 0; i < arguments.length; i++) {
var value = arguments[0];
}
});
This is what i do not want to do.
if (apiRequestList.length === 1) {
$.ajax({
});
} else {
$.when.apply(null, apiRequestList).then(function () {
for (var i = 0; i < arguments.length; i++) {
var value = arguments[0];
}
});
}
You can simply convert arguments into an array, when the length of apiRequestList is 1:
$.when.apply(null, apiRequestList).then(function() {
var _arguments = Array.prototype.slice.call(arguments);
if (Array.isArray(apiRequestList) && apiRequestList.length === 1)
_arguments = [arguments];
for (var i = 0; i < _arguments.length; i++) {
var value = _arguments[i][0];
console.log(value);
}
});
Live Example on jsFiddle (since we can't do ajax on Stack Snippets):
function x(a) {
return $.post("/echo/html/", {
html: "a = " + a,
delay: Math.random()
});
}
function doIt(apiRequestList) {
$.when.apply(null, apiRequestList).then(function() {
var _arguments = arguments;
if (Array.isArray(apiRequestList) && apiRequestList.length === 1)
_arguments = [arguments];
for (var i = 0; i < _arguments.length; i++) {
var value = _arguments[i][0];
console.log(value);
}
console.log("----");
});
}
doIt([x(1), x(2), x(3)]);
doIt([x(4)]);
Example output (it'll vary because of the Math.random()):
a = 4
----
a = 1
a = 2
a = 3
----

how can i return the count from a function

hi iam new to javascript, i am trying to return a count from the function my code is like below
my code
function moredbCount(contentMoreArray2, ArrHeading) {
var sampleArr = [];
for (var a = 0; a < contentMoreArray2.length; a++) {
if (ArrHeading !== 'More') {
var fullHeading = ArrHeading + '-' + contentMoreArray2[a].name;
} else {
fullHeading = contentMoreArray2[a].name;
}
sampleArr.push(fullHeading);
}
var sampleCount = sampleHeadingCount(sampleArr);
return sampleCount.then(function (resultantCount) {
return resultantCount; //Here iam getting some count like 10 and returning it to the function;
});
}
var contentCount;
var totalCount = moredbCount(contentMoreArray2, ArrHeading);
totalCount.then(function (resultantTotalCount) {
return contentCount = resultantTotalCount
});
// Here i want to use contentCount 10, But iam getting undefined
Thanks In advance
return contentCount = resultantTotalCount won't return the count, but rather the response of assignment. In contentCount = resultantTotalCount, you are basically assigning the value of resultantTotalCount to contentCount.
You should use
function moredbCount(contentMoreArray2, ArrHeading) {
var sampleArr = [];
for (var a = 0; a < contentMoreArray2.length; a++) {
if (ArrHeading !== 'More') {
var fullHeading = ArrHeading + '-' + contentMoreArray2[a].name;
} else {
fullHeading = contentMoreArray2[a].name;
}
sampleArr.push(fullHeading);
}
var sampleCount = sampleHeadingCount(sampleArr);
return sampleCount.then(function (resultantCount) {
return resultantCount; //Here iam getting some count like 10 and returning it to the function;
});
}
var contentCount;
var totalCount = moredbCount(contentMoreArray2, ArrHeading);
totalCount.then(function (resultantTotalCount) {
return resultantTotalCount
});

promises and for loops

I have this code:
function ProductObj(product, i) {
var self = this;
self.photo = product.photos.medium_half;
self.title = product.name;
self.tagline = product.tagline;
self.url = product.url;
self.htmlView = "";
self.index = i;
//this async call is slow, very slow
self.updateHTML = function() {
return new Promise(resolve => {
$.get('product-template.html', function (template) {
self.htmlView = template.replace('{image}', self.photo)
.replace('{title}', self.title)
.replace('{tagline}', self.tagline)
.replace('{url}', self.url);
console.log('updateHTML ' + self.index + ' ran');
resolve();
});
});
};
}
when self.updateHTML is called, self.updateDOM is called at the same time
self.updateDOM = function() {
return new Promise(resolve => {
var thisHTML = '';
for( var i = 0; i < self.products.length; i++) {
if (i % 3 === 0 ) {
thisHTML += "<div class='row'>";
console.log('START')
}
thisHTML += self.products[i].htmlView;
if ((i % 3 === 2) || i === (self.products.length - 1) ) {
thisHTML += "</div>";
console.log('finish')
}
if(i === (self.products.length -1)) {
$("#content").append(thisHTML);
}
}
resolve();
})
}
naturally, I used promises to attempt to fix this as such
page.getProducts('data.json')
.then( page.updateProductHTML )
.then( page.updateDOM )
.then( someOtherFunction );
Page.getProducts executes first and returns a promise to page.updateProductHTML. So far my promise in page.updateProductHTML is resolving before assignments can finish in self.updateHTML and updateDOM is firing but it needs values from updateHTML before it can finish
The problem arises from this page.updateProductHTML as it runs this code
self.updateProductHTML = function() {
return new Promise(resolve => {
for( var i = 0; i < self.products.length; i++){
self.products[i].updateHTML();
}
resolve();
})
};
I attempted to wrap the above code in a promise and resolve outside of the for loop but the $.get() call is still working
from what I understand I need to keep updateHTML in a promise but it doesn't seem to do anything different in its present state since I can't use a .then in my for loop in page.updateProductHTML
How do I force page.updateProductHTML to not resolve until it finishes its calls in self.updateHTML?
small recap I want this order self.getProducts() => self.updateProducts => sef.updateDOM => other functions
You should use Promise.all():
self.updateProductHTML = function() {
return Promise.all(self.products.map(product => product.updateHTML()));
};
You could use promise.all, which waits till all of your promises are resolved and then executes the other methods which are dependent on the earlier methods.
An example is here on my Codepen link
self.updateProductHTML = function() {
var updateMethods = [];
for (var i = 0; i < self.products.length; i++) {
updateMethods.push(self.products[i].updateHTML());
}
return Promise.all(updateMethods);
}
self.updateDOM = function() {
for (var i = 0; i < self.products.length; i++) {
if (i % 3 === 0) {
thisHTML += "<div class='row'>";
console.log('START')
}
thisHTML += self.products[i].htmlView;
if ((i % 3 === 2) || i === (self.products.length - 1)) {
thisHTML += "</div>";
console.log('finish')
}
if (i === (self.products.length - 1)) {
$("#content").append(thisHTML);
}
}
}
updateProductHTML().then(updateDOM);

Remove elements from inside Array prototype

I'm trying to extend js native array inside angular service to add some extra features without prototyping global objects.
app.factory('Collection', function($http, $q) {
var Collection = function(arr) {
this.key = 'id';
this._last = 0;
this._first = 77777777; //just big number.
this.append(arr);
}
Collection.prototype = new Array;
Collection.prototype.orderBy = function(n, reverse) {
if (reverse) {
this.sort(function(a, b) {
return b[n] - a[n];
})
} else {
this.sort(function(a, b) {
return a[n] - b[n];
})
}
}
Collection.prototype.spliceBy = function(key, val) {
for (var i = 0; i < this.length; i++) {
if (this[i][key] !== val) {
this.splice(i, 1); ///THIS NEVER HAPPENS !!
console.log('removed ' + i + ' from ', this);
}
}
}
Collection.prototype.subset = function(key, val) {
return this.filter(function(v) {
return (v[key] === val);
});
}
Collection.prototype.add = function(obj) {
for (var i = 0; i < this.length; i++) {
if (this[i][this.key] > this._last) {
this._last = this[i][this.key];
}
if (this[i][this.key] < this._first) {
this._first = this[i][this.key];
}
if (this[i][this.key] === data[this.key]) {
if (override) {
this[i] = data;
console.log('updated uniquePush');
}
return i;
break;
}
}
var id = this.push(data) - 1;
data._index = id;
return id;
}
return collection
});
This is working fine except for the spliceBy function.
I need to filter out elements that does not have value = x;
For example in my controller
.controller(function($scope,Collection){
$scope.posts = new Collection;
$scope.posts.add({id:1,type:'post'});
$scope.posts.add({id:2,type:'comment'});
//Collection is now [{id:1,type:post},{id:2,type:comment}];
//i want to remove all comments from array
$scope.posts.spliceBy('type','comment');
});
Yet nothing happens when calling spliceBy :*(
The spliceBy function will not work if you have two elements to remove in a row, because splice is updating the indexes from i to array.length. Try this instead:
Collection.prototype.spliceBy = function(key, val) {
var i = this.length;
while (i--) {
if (this[i][key] !== val) {
this.splice(i, 1); ///THIS NEVER HAPPENS !!
console.log('removed ' + i + ' from ', this);
}
}
}

How to call function after API gets are finished inside of for loop?

Below is a for loop which will run a max of time times, Inside of that for loop I make a GET call to return some data that needs to be added to my obj object.
I need some way to tell when all 3 GETS are finished as well as the for loop before calling the TagFactory.buildSavedView(obj) line. Thoughts?
for (var i = 0; i < termIDs.length; i++) {
ApiFactory.getTagData(tickers[i], termIDs[i]).then(function(data) {
singleTagArray.push(data.data.tickers);
var updatedTag = TagFactory.renderDirections(singleTagArray, null, period);
newTagObject = updatedTag[0];
tags.push(newTagObject);
finishObjSetup(tags);
console.log('viewHeaderDirect > obj: ', obj);
});
}
TagFactory.buildSavedView(obj);
vm.loadSavedModal = false;
You need to use $q.all, but creating a promise array and pass it to $q.all that will execute its .then only when all the promises gets executed.
Code
var promises = [];
for (var i = 0; i < termIDs.length; i++) {
var promise = ApiFactory.getTagData(tickers[i], termIDs[i]).then(function(data) {
singleTagArray.push(data.data.tickers);
var updatedTag = TagFactory.renderDirections(singleTagArray, null, period);
newTagObject = updatedTag[0];
tags.push(newTagObject);
finishObjSetup(tags);
console.log('viewHeaderDirect > obj: ', obj);
});
promise.push(promise); //creating promise array.
}
$q.all(promise).then(function(){
//here the call will goes after all calls completed.
})
You could use a simple counter:
var y = 0;
for (var i = 0; i < termIDs.length; i++) {
ApiFactory.getTagData(tickers[i], termIDs[i]).then(function (data) {
y++;
singleTagArray.push(data.data.tickers);
var updatedTag = TagFactory.renderDirections(singleTagArray, null, period);
newTagObject = updatedTag[0];
tags.push(newTagObject);
finishObjSetup(tags);
console.log('viewHeaderDirect > obj: ', obj);
if (y === termIDs.length) {
TagFactory.buildSavedView(obj);
vm.loadSavedModal = false;
}
});
}

Categories

Resources