Check array of objects for a specific property value - javascript

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);
}

Related

issues with a global array and sending json data

var x = document.getElementsByTagName('button');//return button array
//arrays
var entry_age = [];//age
var entry_relation = [];//relation
var entry_smoker = [];//smoker
//add button clicked
x[0].addEventListener("click", function(){
var age = document.getElementsByName("age")[0].value;//pull age from box
var relation = document.getElementsByName("rel")[0].value;//pull relation
let smoker = document.querySelector('[name=smoker').checked;
//check relation
if(relation === "")
{
alert("please select a realation");
}
//check to see if age < 0
if(age < 0 || age === " ")
{
alert("age not applicable");
}
//add data to arrays
entry_age.push(age);
entry_relation(relation);
entry_smoker(smoker);
alert(entry_age[0]);
});
x[1].addEventListener("click", function(){
var age = JSON.stringify(entry_age);
alert(entry_age[1]);
document.getElementbyClassName("debug").innerHTML = JSON.stringify(entry_relation);
document.getElementByClass("debug").innerHTML = JSON.stringfy(entry_smoker);
});
I'm trying to store a value in entry age dynamically and convert that to JSON and display it in a only thing is I can't get the data to store in the array, how do I get the array to work globally? At this point I'm pretty sure it's my array but do y'all see anything wrong with the JSON. For debugging purposes I used an alert with the array index and the one in the second function is coming up as unidentified It is to return to a pre tag with the class name of debug. You guys have really helped me out a lot with this project.

Iterating over and comparing properties of two arrays of objects

I have set up a HBS helper which takes in two arrays of objects (users privileges). What I want to do is compare them and inject back into the template the privileges the user does and doesn't have.
Presently I can compare the names of the privileges with the following code:
hbs.registerHelper('selected', function(option, value){
var i;
var j;
var privName;
var userPriv;
var privObj = new Object();
var privArray = [];
for(i in option){
console.log('each ' + JSON.stringify(option[i]));
privName = option[i].privname;
for (y in value){
if(privName == value[y].privname){
userPriv = value[y].privname;
console.log('user has the following privileges', value[y].privname);
privObj = new Object();
privObj.name = userpriv;
privObj.id = value[y]._id;
privObj.state = 'selected';
privArray.push(privObj);
} else if (privName != value[y].privname){
console.log('user doesnt have priv ', privName);
privObj = new Object();
privObj.name = option[i].privname;
privObj.id = option[i].id;
privObj.state = '';
privArray.push(privObj);
}
}
}
console.log('privileges array ', privArray);
return privArray;
});
This works OK when the user only has one privilege, however when the user has more than one, for example two privileges, it returns the privileges twice. If the user has 3, thrice and so on. I know this is because the array is looping again because their are 2, 3 etc in the .length. However I can't seem to find an adequate solution.
Any help?
P.S. it would be nice if the Array.includes() method allowed you to search object properties.
The problem creating new objects the way you did is that for each property you add to your privilege-entity you will have to return to that function and set that property as well. You can instead just add/alter the state property of the existing objects:
hbs.registerHelper('selected', function(option, value) {
var names = option.map(function(opt) {
return opt.privname;
});
value.forEach(function(val) {
val.state = names.indexOf(val.privname) >= 0 ? 'selected' : '';
});
return value;
});
Basically:
The variable names is being mapped to be an array only with the privnames. You can check by using console.log(names).
The Array.forEach() function is helpful in this case because you just need to iterate over each object inside value and set its state-property.
To check if the privname exists, you just need to check the index in the previous names-mapped-array. For such a simple thing I used ternary operator (?:).
Finally, you return value, which is the array containing the objects you had updated.

Different names for different objects of same type

I have an array of objects which are all the type 'person' but I would like the array to contain maybe something like 'person0', 'person1', etc. I currently have this:
var population = [];
var populationCount = 0;
function person(id,age){
//simplified version
this.id = id;
this.age = age;
}
function createPerson(){
population[populationCount] = new person();
population[populationCount].id = populationCount;
population[populationCount].age = 0;
populationCount ++;
}
for(var i = 0; i < 10; i++){
createPerson();
}
The array currently contains "person, person, person, ..." but I would like it to contain "person0, person1, person2, ...".
Why I think this would be useful ...
if let's say population[100] would die, that would be the person with the id of 100, population[101] would take its place, assuming I just simply use population.splice[100] when he dies. So now population[100] has the id 101 and now it would be useful if the array of population would contain different 'names' so you could use indexOf to get the index of any specific person ...
You are confusing a type with an identifier.
Each instance that you make is an instance of a person, that's not going to change. Object types are object types - - person[6] is still an instance of a person.
The id property or an index would be the best approaches to differentiate one instance of person from the next.
Also, your structure is a bit off here. person is a constructor function that takes two arguments, but you don't pass those arguments when you construct a person. Instead you are setting them as properties, which is OK, but counter-intuitive to the way your code is written. And, your createPerson function should decouple the creation of the person from the addition of it to the array.
This structure would be a better approach:
var population = [];
var populationCount = 0;
// By convention, constructor funcitons should be Pascal-Case
function Person(id,age){
//simplified version
this.id = id;
this.age = age;
}
function createPerson(){
// To make this function more true to its name, just have it return
// a new Person. It shouldn't be this funciton's job to know where
// or how that Person is going to be used.
// Also, just pass the arguments that the function is expecting
return new Person(++populationCount, 0);
}
for(var i = 0; i < 10; i++){
// Here, you have a plan for how to use the Person, so here is where
// you should add it to the array. By decoupling the addition of the
// Person to the array from the creation of the Person, the "createPerson"
// function becomes more useful.
population[populationCount] = createPerson();
}
console.log("The population consists of: ", population);
console.log("The last person in the population is: ", population[population.length - 1]);
console.log("The third person in the population is a: ", population[2].constructor.name);
console.log("The third person in the population has an id of: " + population[2].id);
If you are concerned about indexes not matching ids, you could always create a "reset" function, like this:
var population = [];
var populationCount = 0;
function Person(id,age){
this.id = id;
this.age = age;
}
function createPerson(){
return new Person(++populationCount, 0);
}
for(var i = 0; i < 10; i++){
population[populationCount] = createPerson();
}
// Now, kill off the first 5 people:
population.splice(0,5);
console.log("First 5 people are gone. Array now contains: " + population.length + " people.");
console.log("First person's id is: " + population[0].id);
// *****************************************************************************
// Create a new array with the old members, but update the ids
var newPopulation = [];
function rePopulate(ary){
newPopulation = ary.map(function(element){
element.id = newPopulation.length + 1;
return element;
});
}
rePopulate(population);
console.log("The array has been recreated and ids have been adjusted");
console.log("First person's id is now: " + newPopulation[0].id);
And, if you want to be able to find a Person in the array based on the id without knowing what the corresponding index is, you can do this:
var population = [];
var populationCount = 0;
function Person(id,age){
this.id = id;
this.age = age;
}
function createPerson(){
return new Person(++populationCount, 0);
}
for(var i = 0; i < 10; i++){
population[populationCount] = createPerson();
}
// Now, kill off the first 5 people:
population.splice(0,5);
console.log("First 5 people are gone. Array now contains: " + population.length + " people (id's 6 - 10).");
console.log("First person's id is: " + population[0].id);
// *****************************************************************************
// With a simple "find" function, you can locate the correct person based on
// their id and you don't have to worry about the index at all.
function findPerson(id) {
return population.find(function(p){
return p.id === id;
});
}
var x = 7; // Whatever id you are looking for
console.log("Person with id of 7 is: ", findPerson(x));
By saying you would like to have person0, person1, person2, ... You are saying you want infinite number of different types, not just different objects. I do not know of a way to dynamically create types. As said in the comments, you would have to key-value pairs. Why not give each person a unique identifier, which I believe you are already doing.
You could have those objects nested within an object, say:
var persons = [
{
person0:
{
id: 0,
age: 69,
name: 'john doe'
},
person1:
{
...
},
...
}
]

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();

Parse - Javascript Nested Calls

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)
);
}

Categories

Resources