Why are my object-variables null in console log? - javascript

I am seriously having a mental breakdown over this right now...
I am working on a small vue based website.
In this particular Section I want to add an Item to a List.
Here is my Code:
addItem() {
this.items.push(this.itemToBeProcessed);
this.itemToBeProcessed.id = null;
this.itemToBeProcessed.name = "";
this.itemToBeProcessed.price = null;
this.itemToBeProcessed.buyers = [];
this.itemToBeProcessed.amountPurchased = null;
},
async checkInput() {
let item = this.itemToBeProcessed;
if (item.name != "" &&
item.id != null &&
item.price != null &&
item.amountPurchased != null &&
item.buyers != []) {
console.log(this.itemToBeProcessed.id)
console.log(this.itemToBeProcessed)
console.log(this.itemToBeProcessed.id)
this.addItem();
} else {
alert("Not all Fields are set correctly!")
}
}
My Data Structure:
data() {
return {
itemToBeProcessed: {
id: null,
name: "",
price: null,
amountPurchased: null,
buyers: [],
},
items: [],
}
}
As you can see, I tried console logging my itemToBeProcessed.
In the checkInput method I have three console logs. My problem is now that the console writes the correct id every time.
The Item I want to log on the other hand is only correctly logged if I either comment out the resets in addItem() or if I completly comment out the method call. Otherwise all attributes are null, or [].
I have literally no idea how this would be my mistake at all.
Thank you for a response!

When you add your item with items.push, you don't push a copy in your list, you push a reference to the same object as this.itemToBeProcessed.
So, when you reset all fields on this.itemToBeProcessed right after the push, then you're also resetting the fields of the element in the list, since it refers to the same object!
A possible solution could be to use a shallow copy:
this.items.push(Object.assign({}, this.itemToBeProcessed));
Here you push a new object that has all the fields copied from this.itemToBeProcessed, this should solve your issue
Below find an example to put into light the underlying issue:
// With an element as a simple type like string, no issue here.
// I'll change element after being pushed, the list stays "the same".
let list = [];
let element = "aaa";
list.push(element);
console.log(list);
element = "bbb";
console.log(list);
element = null;
console.log(list);
// But if we're dealing with objects, changing element would "change the content" of the list as well.
// Objects variables hold _references_ to an object, they internally refer to the same one!
list = [];
element = { name: "aaa" };
list.push(element);
console.log(list);
element.name = "bbb";
console.log(list);
element.name = null;
console.log(list);
In your code:
function addItem() {
console.log("(addItem) Items before push:");
console.log(this.items);
console.log("(addItem) Items after push:");
this.items.push(this.itemToBeProcessed);
console.log(this.items);
this.itemToBeProcessed.id = null;
this.itemToBeProcessed.name = "";
this.itemToBeProcessed.price = null;
this.itemToBeProcessed.buyers = [];
this.itemToBeProcessed.amountPurchased = null;
console.log("(addItem) Items after having mutated itemToBeProcessed:");
console.log(this.items);
}
this.items = [];
this.itemToBeProcessed = { id: 123, name: "hello", price: 2.1, buyers: [3, 4, 5]};
console.log("Initial:")
console.log(this.items);
console.log(this.itemToBeProcessed);
console.log("calling addItems...");
addItem(this.itemToBeProcessed);
console.log("after addItems:");
console.log(this.items);
console.log(this.itemToBeProcessed);

Related

problems filling an array with javascript

I parsed a json and I'm trying to take 2 values for each element from the json and put them in a array the problem is that I want to put the values into the array like a single element "array" example:
[
{ name: 'name1', elements: [ 'elem1' ] },
{ name: 'name2', elements: [ 'elem2', 'elem3' ] }
]
I tried 2 ways.
the first is this:
function getMonsters(json) {
var monsters = [];
var monster = {};
json.forEach(element => {
if (element.type === "large") {
monster['name'] = element.name;
monster['elements'] = element.elements;
monsters.push(monster);
}
});
return monsters;
}
the problem with the first way is that it always returns the same 2 values:
the second way is this:
function getMonsters(json) {
var monsters = [];
var monster = {};
json.forEach(element => {
if (element.type === "large") {
monsters.push(element.name, element.elements);
}
});
return monsters;
}
but the problem with the second way is that it returns each monster and element separately and not like in my example:
this is the json if u want to check : https://mhw-db.com/monsters
You are reusing the monster object every iteration in your first example. Either move the declaration of var monster = {} into the loop or, better yet, just push an object literal.
function getMonsters(json) {
const monsters = [];
json.forEach(({ elements, name, type }) => {
if (type === "large") {
monsters.push({ name, elements });
}
});
return monsters;
}
Your first attempt is almost correct. The reason why all of the items in the array end up being the same object is because monster is the same reference in all of the array items. You need a new instance of monster on every iteration. Just put your initialization of monster in your loop
function getMonsters(json) {
var monsters = [];
json.forEach(element => {
if (element.type === "large") {
var monster = {};
monster['name'] = element.name;
monster['elements'] = element.elements;
monsters.push(monster);
}
});
return monsters;

Remove singular element from an object's key array

I have an object that has multiple keys and each of these keys has an array storing multiple elements. I want to be able to remove a specified element from the key's array.
I have tried using the delete keyword as well as the filter method, but I have been unsuccessful. I'm a total newbie to JS so I appreciate any assistance. Also, I want to do this using ONLY JavaScript, no libraries.
Here is the code where I am creating my object:
function add(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() +
weekdayDue.slice(1);
if (toDoList[capitalWeekday] === undefined) {
let subArr = [];
toDoList[capitalWeekday] = subArr.concat(task);
} else {
toDoList[capitalWeekday].push(task);
}
}
and here is the code as I have it now. Clearly it is not producing the correct result:
function remove(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() +
weekdayDue.slice(1);
delete toDoList.capitalWeekday[task]
//the below code is working; i want to send this to another
array
if (archivedList[capitalWeekday] === undefined) {
let subArr = [];
archivedList[capitalWeekday] = subArr.concat(task);
} else {
archivedList[capitalWeekday].push(task);
}
};
add('laundry', 'monday');
add('wash car', 'monday');
add ('vacuum', 'tuesday');
add('run errands', 'wednesday');
add('grocery shopping', 'wednesday');
// the output is: { Monday: [ 'laundry', 'wash car' ],
Tuesday: [ 'vacuum' ],
Wednesday: [ 'run errands', 'grocery shopping' ] }
Then let's say I want to remove 'wash car' from Monday I was trying:
remove('wash car', 'monday');
console.log(toDoList)
// The output is an empty object {}
I personally would refactor a bit your code, but I've worked a bit around it to fix some issues.
First of all, you shouldn't use delete for your scenario, because it will reset the item at the nth position of the array with the default value, which is undefined.
Usually, for that kind of operations, since you deal with strings, you rather take a look at the first occurrence of your item in the array, take its index, and use splice (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) to actually remove the item from the array.
In this way, you end up with a clean array without invalid items in it.
Below is the working code (with the mentioned fixes) that does what you asked. As a side note, I would suggest you to avoid working with strings for such purposes, but I would rather tackle objects with unique ids, so that it's significantly easier to keep track of them between arrays and objects.
Additionally, there are some cases that you didn't think about, for instance I can think about calling remove by giving an invalid task, so you may work a bit around the code below to handle the case where taskIndex is -1 (meaning that no item was found with that index).
var toDoList = {}, archivedList = {};
function add(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() + weekdayDue.slice(1);
if (toDoList[capitalWeekday] === undefined) {
let subArr = [];
toDoList[capitalWeekday] = subArr.concat(task);
} else {
toDoList[capitalWeekday].push(task);
}
}
function remove(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() + weekdayDue.slice(1);
let taskIndex = toDoList[capitalWeekday].indexOf(task);
toDoList[capitalWeekday].splice(taskIndex, 1);
//delete toDoList[capitalWeekday][taskIndex];
if (archivedList[capitalWeekday] === undefined) {
let subArr = [];
archivedList[capitalWeekday] = subArr.concat(task);
} else {
archivedList[capitalWeekday].push(task);
}
};
add('test', 'monday');
add('wash car', 'monday');
remove('wash car', 'monday');
console.log(toDoList);
console.log(archivedList);
You are on the right path. Maybe the trouble you had with filter is because filter will return a new Array and not modify the current one. You could update your remove function and replace the line:
delete toDoList.capitalWeekday[task]
with
toDoList.capitalWeekday = toDoList.capitalWeekday.filter((item) => {return item !== task});
function remove(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() +
weekdayDue.slice(1);
// Assign new array with all elements but task
toDoList[capitalWeekday] = toDoList[capitalWeekday].filter(i => i !== task)
};
add('foo'...
add('bar'...
"{
"Baz": [
"Foo",
"Bar"
]
}"
remove('foo'...
"{
"Baz": [
"Bar"
]
}"

Problem in adding object properties to specific object inside array in JavaScript

I have an object in JavaScript that looks something like this
{
product_id: "2",
product_name: "Drinks"
}
The name of the object is product.
There is an array that contains entries of the above object. So, each array item is an entry of the above object.
On button click I check if an object entry with a particular product_id (that is being searched) exists in the array or not. If the object with the particular product_id does not exist in the array then I have to add this new object in to the array. Whereas if the object entry with the particular product_id exists then first I have add a new property named "qty" to the object and then this object is to be added as the new entry in to the array.
Below is the code on button click.
I console.log() the array to see the result.
When the button is clicked the first time then I get the array entry correctly where it shows the object inside the array.
When the button is clicked the second time then the code goes in to the else condition and a new property (by the name qty) is added to the object and then the object is added in to the array. So, now the array has two object entries (first one is added through if condition and the second one is added through the else condition).
Strangely, the problem is that when the second time button is clicked and the else condition is executed then the code modifies the previous existing object entry (which already is there in the array) and adds qty property in that object entry as well.
Ideally it should treat these two as separate entries and if I modify the second object entry then the first entry (which already exists in the array) should remain as it is (which means without qty property) whereas it modifies the previous entry too and adds new one too.
OnButtonClick() {
if (array.length === 0) {
array.splice(0, 0, product);
}
else {
product.qty = 1;
array.splice(0, 0, this.product);
}
}
Below is the full code:
// First Page: categories.ts sets the existing product object using a service
// then navigates to the second page product.ts
ButtonClick() {
this.Service.setProduct(product);
this.route.navigate(['products']);
}
// Service page: service.ts
export class ManageService {
products: any;
ProductArray: any = [];
constructor() { }
public setProduct(data) {
this.products = data;
}
public getProduct() {
return this.products;
}
}
//Second page: products.ts
// First it gathers the product object details that were passed from previous
// categories.ts using getProduct() method in the service.ts
export class ProductsPage implements OnInit {
product: any = [];
ngOnInit() {
this.product = this.Service.getExtras();
}
ButtonClick(searchid: any) {
// searchid is passed on button click
let findsearchidarr = FindItem(searchid);
if (findsearchidarr[0] === true) {
this.Service.ProductArray[findsearchidarr[1]].quantity =
++this.Service.ProductArray[findsearchidarr[1]].quantity;
this.router.navigate(['categories']);
}
else if (findsearchidarr[0] === false) {
this.product.quantity = 1;
this.Service.ProductArray.splice(0, 0, this.product);
this.router.navigate(['categories']);
}
}
FindItem (searchid: any) {
let i = 0;
let foundarray: any = [];
for (let items of this.Service.ProductArray) {
if (items.search_id.toLowerCase().includes(searchid)) {
foundarray[0] = true;
foundarray[1] = i;
foundarray[2] = items.product_id;
return foundarray;
}
i++;
}
foundarray[0] = false;
foundarray[1] = -1;
foundarray[2] = 0;
return foundarray;
}
}
See the logic below. It adds the quantity property to already existing object otherwise adds a new object to array.
products: any[] = [{
product_id: "2",
product_name: "Drinks"
},
{
product_id: "3",
product_name: "Wafers"
},
{
product_id: "4",
product_name: "Chocolates"
}
];
productIDToSearch:number = 4;
quantityToAdd: number = 20;
let foundIndex = products.findIndex((val) => val.product_id == productIDToSearch);
if(this.foundIndex >= 0) {
this.products[this.foundIndex].quantity = this.quantityToAdd;
}
else {
this.products.push({product_id:productIDToSearch, product_name:'Ice-cream', quantityToAdd: 33});
console.log(products);
}
Equivalent javascript code
var products = [{
product_id: "2",
product_name: "Drinks"
},
{
product_id: "3",
product_name: "Wafers"
},
{
product_id: "4",
product_name: "Chocolates"
}
];
let productIDToSearch = 5;
let quantityToAdd = 20;
let foundIndex = products.findIndex((val) => val.product_id == productIDToSearch);
if(foundIndex >= 0) {
products[foundIndex].quantity = quantityToAdd;
console.log(products);
}
else {
products.push({product_id:productIDToSearch, product_name:'Ice-cream', quantityToAdd: 33});
console.log(products);
}
The issue is that in JavaScript objects are treated by reference and so it creates confusion (at least in my case) when I try to add any property or modify any property inside the array of objects.
The solution I found was to first copy the object to another object so that the "by reference" possibility is ruled out, then use the new "copied" object to mark a new entry. This solved the issue.

AngularJS: Merge object by ID, i.e. replace old entry when IDs are identical

I am using Ionic with AngularJS and I am using a localForage database and AJAX via $http. My app has a news stream that contains data like this:
{
"feed":[
{
"id":"3",
"title":"Ein Hund",
"comments:"1"
},
{
"id":"2",
"title":"Eine Katze",
"comments":"2"
}
],
"ts":"20150907171943"
}
ts stands for Timestamp. My app saves the feed locally via localForage.
When the app starts it first loads the locally saved items:
$localForage.getItem("feed").then(function(val) { vm.feed = val; })
Then, it loads the new or updated items (ts < current timestamp) and merges both the old and new data:
angular.extend(vm.feed, response.data.feed);
Updated items look like this:
{
"feed":[
{
"id":"2",
"title":"Eine Katze",
"comments":"4"
}
],
"ts":"20150907171944"
}
That is, the comments count on feed item 2 has changed from 2 to 4. When I merge the old and new data, vm.feed has two items with id = 2.
Does angularjs has a built-in "merge by id" function, i. e. copy from source to destination (if it is a new element), or otherwise replace the old element? In case angularjs does not have such a function, what's the best way to implement this?
Thanks in advance!
angular.merge(vm.feed, response.data.feed);
// EDIT
Probably, it will not merge correctly, so you have to update all properties manually. Update ts property and then find your object with id and replace it.
There is no builtin, I usually write my own merge function:
(function(){
function itemsToArray(items) {
var result = [];
if (items) {
// items can be a Map, so don't use angular.forEach here
items.forEach(function(item) {
result.push(item);
});
}
return result;
}
function idOf(obj) {
return obj.id;
}
function defaultMerge(newItem, oldItem) {
return angular.merge(oldItem, newItem);
}
function mergeById(oldItems, newItems, idSelector, mergeItem) {
if (mergeItem === undefined) mergeItem = defaultMerge;
if (idSelector === undefined) idSelector = idOf;
// Map retains insertion order
var mapping = new Map();
angular.forEach(oldItems, function(oldItem) {
var key = idSelector(oldItem);
mapping.set(key, oldItem);
});
angular.forEach(newItems, function(newItem) {
var key = idSelector(newItem);
if (mapping.has(key)) {
var oldItem = mapping.get(key);
mapping.set(key, mergeItem(newItem, oldItem));
} else {
// new items are simply added, will be at
// the end of the result list, in order
mapping.set(key, newItem);
}
});
return itemsToArray(mapping);
}
var olds = [
{ id: 1, name: 'old1' },
{ id: 2, name: 'old2' }
];
var news = [
{ id: 3, name: 'new3' },
{ id: 2, name: 'new2' }
];
var merged = mergeById(olds, news);
console.log(merged);
/* Prints
[
{ id: 1, name: 'old1' },
{ id: 2, name: 'new2' },
{ id: 3, name: 'new3' }
];
*/
})();
This builds a Map from the old items by id, merges in the new items, and converts the map back to list. Fortunately the Map object will iterate on the entries in insertion order, according to the specification. You can provide your idSelector and mergeItem functions.
Thanks hege_hegedus. Based on your code, I've written my own and tried to use less loops to speed things up a bit:
function updateCollection(localCollection, fetchedCollection) {
angular.forEach(fetchedCollection, function(item) {
var append = true;
for (var i = 0; i < localCollection.length; i++) {
if (localCollection[i].id == item.id) {
// Replace item
localCollection[i] = item;
append = false;
break;
} else if (localCollection[i].id > item.id) {
// Add new element at the right position, if IDs are descending check for "< item.id" instead
localCollection.splice(i, 0, item);
append = false;
break;
}
}
if (append) {
// Add new element with a higher ID at the end
localCollection.push(item);
// When IDs are descending use .unshift(item) instead
}
});
}
There is still room for improvements, i. e. the iteration through all the objects should use binary search since all items are sorted by id.

how to push json data into array with 2 level or n level

It may be a silly question, but right now I can't figure it out. so I'm in need.
I'm showing static data but i'd like to make it dynamic in future.
var name = [];
name.push({ name: "Gareth" });
name[0].push({ name: "john" }); // This statement doesn't work as name[0]!=[] I guess.
name[0].push({name:"dolly"});
I want to get an out put like,
+name <--- expandable
+0 <--- expandable
name : "gareth"
+0 <--- expandable
name: "john"
+1 <--- expandable
name: "dolly"
I know its not a difficult one. But i'm unable to figure it out right now. Help would be greatly appreciated.
You can't push anything onto name[0] as it is not an array but you can still assign elements with number's to it and create your own push.
var makePushable = function (obj) {
obj.push = function (item) {
this[this.length] = item;
this.length++;
};
obj.length = 0; //Keep track of how many elements
}
var name = [];
name.push({ name: "Gareth" });
makePushable(name[0]);
name[0].push({ name: "john" });
name[0].push({ name:"dolly" });
And we get:
name[0] ---> {
name:"gareth",
0: {name:"john"},
1: {name:"dolly"},
length: 2,
push: function () { ... }
}
name[0][0] ---> {name:"john"}
name[0][1] ---> {name:"dolly"}
If you don't want push and length to show up on for ( prop in xxx ) if (xxx.hasOwnProperty(prop)), you could make a class like so:
var Pushable = function (obj) {
for (var i in obj) {
this[i] = obj[i];
}
Object.setPrototypeOf(this, {
push: function (item) {
this[this.length] = item;
Object.getPrototypeOf(this).length++;
},
length: 0
});
};
Pushable.prototype.push = function (item) {
this[this.length] = item;
Object.getPrototypeOf(this).length++;
};
Pushable.prototype.length = 0;
var name = [];
name.push(new Pushable({ name:"Gareth" }));
name[0].push(new Pushable({ name: "john" }));
name[0].push(new Pushable({ name:"dolly" }));
name[0][1].push(new Pushable({ name:"last kid" }));
You could make the push function, automatically create a Pushable class for you if you wanted.
FIRST CHANGE YOU ARRAY NAME
try this
var m = {}; m.name=[];
m.name.push({ name: "Gareth" });
m.name.push({ name: "john" }); // This statement doesn't work as name[0]!=[] I guess.
m.name.push({name:"dolly"});
out put

Categories