Parse - Javascript Nested Calls - javascript

I am writing a javascript code using Parse. I have User class and Subscription class. All users' subscription information are stored on Subscription class so it is like one to many relationship.
I would like to retrieve user information and each user's subscription count and store it in an object array.
What I have tried is as follows:
var user = Parse.User;
var query = new Parse.Query(user);
query.find().then(function(objects) {
return objects;
}).then(function (objects) {
for(var i = 0; i < objects.length; i++) {
var object = objects[i];
var subscription = Parse.Object.extend('Subscription');
var queryForSubscription = new Parse.Query(subscription);
queryForSubscription.equalTo('subscriptionUser', object);
queryForSubscription.count().then(function(subscriptions) {
var userEmail = object.get('email');
var userDOB = object.get('userDOB');
var userJoinedAt = object.createdAt;
var userGender = object.get('userGender') ? 'Male' : 'Female';
var subscriptionCount = subscriptions;
var sub = [
userEmail,
userDOB,
userGender,
userJoinedAt,
subscriptionCount
];
userArray.push(sub);
});
}
});
However, userArray only contains last user's information repetively. I reckon it is because wihitn the second query promise (queryForSubscription).count()) runs after the outer for loop finishes.
Let's say I got User A and User B. User A has two subscriptions whereas User B has none.
What I expected is something like this:
[{User A's email, User A's dob, User A's Gender, User A's Joined Date, 2},
{User B's email, User B's dob, User B's Gender, User B's Joined Date, 0}]
However, the result is
[{User A's email, User A's dob, User A's Gender, User A's Joined Date, 2},
{User A's email, User A's dob, User A's Gender, User A's Joined Date, 0}]
I'm stuck at the moment. Have been trying to use Promise and etc but got no idea..Please help!
UPDATE
GOTTA LOVE SO!!! THANK YOU EVERY SINGLE ONE OF YOU GUYS SO MUCH!!
One more question here. Actually, I got one more class called Favourite to get a count of each user's number of favourites.
If the number of User A's favourite is 5 and 3 for User B, the expected result will look like this in the end:
[{User A's email, User A's dob, User A's Gender, User A's Joined Date, 2, 5},
{User B's email, User B's dob, User B's Gender, User B's Joined Date, 0, 3}]
I am not too sure where I put a new query for Favourite class...Need your advice again!

You can go with #Hacketo's answer.
Another hacky way to get around this bug is to use .forEach() :
}).then(function (objects) {
objects.forEach(function(object) {
var subscription = Parse.Object.extend('Subscription');
...
});
});
A new object variable will be allocated for each iteration of the loop, and you should escape the "shared var" bug.
pointing out the bug
One of the common javascript pitfalls is : a local variable's scope is not its closest bloc, it is its closest function.
The following snippet :
function (objects) {
for(var i = 0; i < objects.length; i++) {
var object = objects[i];
...
is actually equivalent to :
function (objects) {
var i, object;
for(i = 0; i < objects.length; i++) {
object = objects[i];
...
So the object variable in the OP's code is actually shared by all the created queryForSubscription variables, and when any of the .then(..) callbacks is executed (after the loop finishes, as the OP correctly pointed out), this object variable holds the value of the last item in the array.

It seem to be that you need to save the value of object when the last functions are executed. Try this :
for(var i = 0; i < objects.length; i++) {
var object = objects[i];
queryForSubscription.count().then(
(function(myObject){
return function(subscriptions) {
var userEmail = myObject.get('email');
...
};
})(object)
);
}

Related

Updating the value of an object inside a loop using javascript

I'm currently facing a difficulty in my codes.
First i have an array of objects like this [{Id:1, Name:"AML", allowedToView:"1,2"}, {Id:2, Name:"Res", allowedToView:"1"}...] which came from my service
I assign it in variable $scope.listofResource
Then inside of one of my objects I have that allowedToView key which is a collection of Id's of users that I separate by comma.
Then I have this code...
Javascript
$scope.listofResource = msg.data
for (var i = 0; i < msg.data.length; i++) {
First I run a for loop so I can separate the Id's of every user in allowedToView key
var allowed = msg.data[i].allowedToView.split(",");
var x = [];
Then I create a variable x so I can push a new object to it with a keys of allowedId that basically the Id of the users and resId which is the Id of the resource
for (var a = 0; a < allowed.length; a++) {
x.push({ allowedId: allowed[a], resId: msg.data[i].Id });
}
Then I put it in Promise.all because I have to get the Name of that "allowed users" base on their Id's using a service
Promise.all(x.map(function (prop) {
var d = {
allowedId: parseInt(prop.allowedId)
}
return ResourceService.getAllowedUsers(d).then(function (msg1) {
msg1.data[0].resId = prop.resId;
Here it returns the Id and Name of the allowed user. I have to insert the resId so it can pass to the return object and it will be displayed in .then() below
return msg1.data[0]
});
})).then(function (result) {
I got the result that I want but here is now my problem
angular.forEach(result, function (val) {
angular.forEach($scope.listofResource, function (vv) {
vv.allowedToView1 = [];
if (val.resId === vv.Id) {
vv.allowedToView1.push(val);
I want to update $scope.listofResource.allowedToView1 which should hold an array of objects and it is basically the info of the allowed users. But whenever I push a value here vv.allowedToView1.push(val); It always updates the last object of the array.
}
})
})
});
}
So the result of my code is always like this
[{Id:1, Name:"AML", allowedToView:"1,2", allowedToView:[]}, {Id:2, Name:"Res", allowedToView:"1", allowedToView:[{Id:1, Name:" John Doe"}]}...]
The first result is always blank. Can anyone help me?
Here is the plunker of it... Plunkr
Link to the solution - Plunkr
for (var i = 0; i < msg.length; i++) {
var allowed = msg[i].allowedToView.split(",");
msg[i].allowedToView1 = [];
var x = [];
Like Aleksey Solovey correctly pointed out, the initialization of the allowedToView1 array is happening at the wrong place. It should be shifted to a place where it is called once for the msg. I've shifted it to after allowedToView.split in the first loop as that seemed a appropriate location to initialize it.

JavaScript - declare variable with for loop item

Is it possible to declare a variable differently for each iteration? Here is the general idea:
var userIds = [9110252, 55829847, 145189041]
for(u = 0; u < userIds.length; u++){
console.log(userIds[u]);
var user+userIds[u] = userIds[u];
}
It's not possible. But you also don't need that:
You won't be generating dynamic variable names, but you can have a different variable in each iteration of the for loop:
var userIds = [9110252, 55829847, 145189041]
for(u = 0; u < userIds.length; u++){
console.log(userIds[u]);
var user = userIds[u];
}
On the first iteration, user will hold 9110252, on the second a new value is set to variable user: 55829847 and so forth.
But in this case, as #adeneo mentioned: You already have: userIds[u] to refer to the value.
We have arrays for that .Why do u need to have different name of variable when one array variable can do it for u and also it makes code easy to manage.
Reading through the comments on the question and wanting to store it inside local storage. I would do this:
var userIds = [9110252, 55829847, 145189041];
for (var i = 0; i < userIds.length; i++) {
var userId = 'user' + userIds[i];
window.localStorage.setItem(userId, userIds[i]);
}
I would recommend however to reconsider this type of storage, because you're now storing redundant data. It's only distinguished with the word "user" in front of it.
User #Abhishek Panjabi also mentioned that this is the reason why we have arrays. He is correct in saying this.
Credits to user #adeno for his comment.

Javascript: Using user input to search an array of arrays

Maybe I'm structuring this code wrong (first time working with Javascript) but I want to take user input and search an array of arrays to check if an array with that name exists.
I first tried using the eval() function which I've been told isn't good but it works when there is a match, failing when a match that doesn't exist though.
The basic premise of the program is there is an array containing locations which are subsequently arrays containing food items. The user enters the name of a food item and where they want to put it and it will create an object of food, insert that object into the right location within the arrays, and also input the item onto a list displayed in html.
Here's all the code so far:
var fridge = [];
var freezer = [];
var pantry = [];
var locations = [fridge, freezer, pantry];
function Food(name, location, locationName){
this.name = name;
this.location = location;
this.locationName = locationName;
this.displayName = function(){
alert(this.name);
};
};
function createFood(){
var name = prompt("Enter the items name:");
var locationName = prompt("Enter a location to store it:")
var location = eval(locationName);
while(locations.indexOf(location) == -1){
alert("This is not an actual location.");
locationName = prompt("Please enter another location:");
location = eval(locationName);
};
var x = new Food(name, location, locationName)
function insertFood(Food){
var a = locations.indexOf(Food.location);
locations[a].push(Food);
var list = document.getElementById(Food.locationName + "_items");
var li = document.createElement("li");
li.innerHTML = Food.name;
list.insertBefore(li, list.lastChild);
};
insertFood(x);
};
Please let me know if this is structured wrong cause this was my idea for structuring at first glance.
Thanks!
As suggested above, it would be best to make locations an object, so that you can have a key (a string) pointing to the array with the same name.
var fridge = [];
var freezer = [];
var pantry = [];
var locations = {
"fridge":fridge,
"freezer":freezer,
"pantry":pantry
};
The benefit of this is that you don't need to have a locationName, since it never really comes into play. All you would need is to check if the locations object has a property by the same name as the user input, using the native hasOwnProperty function. Something like:
if(!locations.hasOwnProperty(userInputLocation))
alert("That is not a designated location");
Then your Food constructor also becomes simpler, and needs only name and location properties:
function Food(name, location){
this.name = name;
this.location = location;
}
You can also then call any specific location directly by its name (if you're going to declare it globally as you did in your code), or more appropriately (if you declare the arrays inside the object as in SGD's code) by locations[location], where location is just a string holding either "freezer" or "pantry" or "fridge". You can also call the array via locations[someFood.location].
Anyway I am not much for prompts and alerts (let alone eval), so I created a jsBin using input fields, you can check it out here: http://jsbin.com/pamoquwuhu/1/edit
edited to add:
If the goal is that you later want to find food by its name in all the locations it is saved in, it would be best to add/push foodObj.name instead of the whole foodObj in locations[location]. Then you can use the native for(property in object) loop on the locations object to see where all a given food might be stored, and push it into a results array. So your findFood function might contain the following (assuming food is the user input string of of food name to search for:
var results = [];
for(var loc in locations){ // loops through the property names in `locations`
if(locations[loc].indexOf(food)>=0)
results.push(loc);
}
if(!results.length){
alert(food+' was not found');
else
alert(food+' was found in '+results.toString());
Assuming the same food can be stored in multiple locations and that you want to find a given food by its name, your food object's location property would become less important (or possibly useless).
You are using Food as function/constructor and as parameter name (that should not be the issue however but can cause trouble later) and you are never calling insertFood. Also locations should rather be object than array so that you can access the sub arrays as you do in your code.
var locations = {fridge : [], freezer:[], pantry:[]];
function Food(name, locationName){
this.name = name;
this.locationName = locationName;
this.displayName = function(){
alert(this.name);
};
};
function createFood(){
var name = prompt("Enter the items name:");
var locationName = prompt("Enter a location to store it:")
while(locationName in locations){
alert("This is not an actual location.");
locationName = prompt("Please enter another location:");
};
var x = new Food(name, locationName)
function insertFood(food){
locations[food.locationName].push(food);
var list = document.getElementById(food.locationName + "_items");
var li = document.createElement("li");
li.innerHTML = food.name;
list.insertBefore(li, list.lastChild);
};
// call the method now?
insertFood(x);
};
createFood();

How to generate an array of arrays in Javascript?

So given:
var person = {name: "", address: "", phonenumber: ""}
I want to create a loop to receive user input (until they decide they don't want to input anymore information and input nothing or click cancel). I'd also like to use the person object as a prototype.
So I guess an object just to store the name/address/phone number of an arbitrary number of people. My logic for this would be to dynamically add an entire array to an object for every iteration of my loop. My code looks something like this:
var person = {name: "", address: "", phonenumber: ""}
var customer = []; //used to store each person
var input = "x";
//loop prompting user to input name/address/phone number
for(var i = 0; input != ""; i++){
var input = prompt("Input separated by commas");
//example input: mike, main, 123456789
var results = input.split(", "); //create an array from the input
//move input into the person array.
//person to look like {name = "mike", address = "main", phone = "123456789"}
person.name = results.shift();
person.address = results.shift();
person.phone = results;
customer[i] = person;//store the person array into the customer array.
}
I've been trying to dynamically generate something like this:
customer =
[{name, address, phone},
{name, address, phone},
{name, address, phone}]
and then be able to access it and print it. i've been trying to access it with
console.log(customer[0].phone);
unfortunately im getting an error.
sorry for my error, console.log prints nothing so it seems like there's nothing stored in customer[0].phone.
i can't get access to any of the data that i've prompted the user for and saved in variables. when i use the alert function all i get is a blank box. whenever i try to print customer i get the message [object Object]or something along those lines
var customer = [];//create array
var person = {};// create object
var input = prompt("...");//your prompt
var results = input.split(", "); //create an array from the input
person.name = results[0];//add result with 0 index
person.address = results[1];//add result with 1 index
person.phone = results[2];//add result with index 2
customer[0] = person;//add the person object to the array.
The above code should do as you wish. I'm having a hard time seeing why you would iterate a prompt to the same user?? So try this first then go on.
http://jsfiddle.net/zkc2swmp/1/
Don't go irratating your users with prompt boxes, use a form. Also don't iterate something like that, and your condition in the for loop basically creates an infinite loop which is also bad
My code could be a lot better, such as if conditions, and exactly how to use this with a form. Though frankly it seems as though you are far from that. So I've started you off, try it and see if you can implement this the way you want, then comment with suggestions for helping you do something else.
First of all, every time you are assigning person to customer i.e.
customer[i] = person;
all other objects are getting modified as your breaking point is input== "" so all objects are modified with null values.
in start you are defining property as "phonenumber" and in the end u r putting value in "phone".
correct code shud be :
var customer = []; //used to store each person
var input = "x";
//loop prompting user to input name/address/phone number
for(var i = 0; input != ""; i++){
var input = prompt("Input separated by commas");
//example input: mike, main, 123456789
var results = input.split(", "); //create an array from the input
var person = {};
person.name = results.shift();
person.address = results.shift();
person.phone = results;
customer[i] = person;//store the person array into the customer array.
}

Check array of objects for a specific property value

I'm creating a small program where I have a page with two input fields and a user can enter their name and the number of calories they've eaten today. After a person enters their data, I want to display it alongside other people's data in a list sorted by total calories.
I also want that person to be able to re-enter their name and additional calories and have it update their total calories (as opposed to creating another entry with their name and most recent calorie amount).
I am assigning the input values to variables, using those to create a new Person object, then pushing that object to an array.
How can I test to see if the array contains a Person object with a name that already exists? My test isn't recognizing names that have already been submitted. If a person's name has already been entered, I'd like to update their total calories instead of creating a new Person object.
My javascript code:
(function () {
"use strict";
/* Person object constructor */
function Person(name, calories) {
this.name = name;
this.calories = calories;
}
function addToList() {
var name = document.getElementById('name').value;
var calories = document.getElementById('calories').value;
/*
Check to see if list already contains person's name
If yes, update their calorie amount.
If not, create a new player and add them to the list.
*/
for (var i = 0; i < list.length; i++) {
if (list[i] === name) {
alert('This person already exists.');
} else {
var newPerson = new Person(name, calories);
list.push(newPerson);
}
}
}
var list = [];
$('#add').click(addToList);
})();
Your list is a list of Person (if rankings.push is meant to be list.push) so when you are doing (list[i] === name) that is trying to compare a Person object to a string literal. Try doing (list[i].name.toLowerCase() === name.toLowerCase())
It looks like you're storing names inside of the array list, however these names are never being ADDED to list. Instead you appear to be using another array called rankings.
rankings.push(newPerson);
list.push(name);
And since you already have an array that stores people, rankings, maybe you should iterate over that?
var foundPerson = false;
for (var i = 0; i < rankings.length; i++) {
if (rankings[i].name === name) {
alert('This person already exists.');
// do your update
rankings[i].calories = calories;
// set flag so we know we actually found a person
foundPerson = true;
break;
}
}
// if no person found, add new person
if (!foundPerson) {
var newPlayer = new Person(name, calories);
rankings.push(newPerson);
}

Categories

Resources