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.
Related
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);
In the snippet below the goal is to check if the addedPlayer already exist in players by checking the .name property
if addedPlayer is already in players : Give the player a point
else update players by adding in addedPlayer with a starting point
let players = []
let addedPlayer = {
name: /* The Given Name To Check in players */ ,
points: 1
}
function addPlayer(playerList, playerToAdd){
const existingPlayers = playerList.map((playerToAdd) => playerToAdd.name);
if(existingPlayers.includes(playerToAdd.name)){
playerToAdd.points++
} else {
playerList.push(playerToAdd)
}
}
addPlayer(players, addedPlayer)
console.log(players)
The problem is players only returns addedPlayer. My Goal being that players will store every new player and adds points accordingly
You can achieve that by finding if the newPlayer already exists in the given array by checking against player.name then conditionally incrementing player.point if newPlayer already exists else adding the newPlayer to the array.
Here is an example.
let players = [
{
name: 'Player1',
point: 1
},
{
name: 'Player2',
point: 2
}
]
let newPlayer = {
name: 'Player2',
points: 1
}
function addPlayer(playerlist, newPlayer) {
// idx will be -1 if a player does not exist in playerlist by the name
const idx = playerlist.findIndex(p => p.name === newPlayer.name);
if (idx === -1) {
// if you want to mutate the original array you can use playerlist.push(newPlayer) then return immediately from the function and omit the next return line
return [...playerlist, newPlayer]
}
// increment player.point if newPlayer already exists
playerlist[idx] = {...playerlist[idx], point: playerlist[idx].point + 1}
// if you want to mutate the original array then omit the next return line
return [...playerlist]
}
// because newPlayer already exists in players array by name calling the function addPlayer will increment player.point in players array where player.name is 'Player2'
console.log(addPlayer(players, newPlayer))
// calling the function with a player that does not exist in the array
let anotherPlayer = { name: 'Player3', point: 0 };
// anotherPlayer will be added to the returned array
console.log(addPlayer(players, anotherPlayer))
P.S. - The current solution will return a new array on each call of the addPlayer function if your use case requires you to mutate the array you can use Array.prototype.push and omit the returns from the function. I have included some comments in the snippet for more details.
It's considered good practice to always initialize our variables. I just created a name variable for our user object.
Each time you run addPlayer it will now push new players to the list.
let players = []
var name = "";
let addedPlayer = {
name: name,
points: 1
}
function addPlayer(playerList, playerToAdd) {
const existingPlayers = playerList.map((playerToAdd) => playerToAdd.name);
if (existingPlayers.includes(playerToAdd.name)) {
playerToAdd.points++
} else {
playerList.push(playerToAdd)
}
}
addPlayer(players, addedPlayer)
console.log(players)
I am having an array of objects and want to remove duplicates
Like in the image i want to remove all duplicate values and get only single values and for the duplicates increase the quantity.
It seems like you are implementing how to add items in your cart. I assume you have a product class or similar name to create your objects.
export class Product {
constructor(
public name: string, public amount: number, quantity: number, productId: string){}
}
I suggest you implement another class, say cart.
export class Cart {
constructor(
public product: Product, public quantity: number) {}
}
In your service.ts,
carts: Cart[];
addToCart(product: Product) {
for (let i = 0; i < this.carts.length; i++) {
if(this.carts[i].product.productId == product.productId) {
this.carts[i].quantity = this.carts[i].quantity + 1;
return;
}// this code here to avoid duplicates and increase qty.
}
let cart = new Cart(product, 1);
this.carts.push(cart);
}
You should have 3 different items in your cart, cs(3), dota(1), nfs(1). In my shopping cart, only 1 per item is added to cart, when you click on the product in my products component.
for(let i=0; i<products.length; i++){
this.addToCart(products[i]);
}
This code works as show below
Assuming arr is the array you want to remove duplicated from, here is a simple code to remove duplicated from it;
arr = Array.from(new Set(arr.map(x => {
let obj;
try {
obj = JSON.stringify(x);
} catch (e) {
obj = x;
}
return obj;
}))).map(x => {
let obj;
try {
obj = JSON.parse(x);
} catch (e) {
obj = x;
}
return obj;
});
Now, arr has no duplicates.
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.
Part of my json Array
var videos = $j.parseJSON('
[
{ "privacy":"public",
"id":"1169341693" },
{ "privacy":"private",
"id":"803641223" },
{ "privacy":"public",
"id":"1300612600" }, ......
When I console.log the element I'm getting
[Object, Object, Object, …]
0: Object
privacy: "public"
id: "1169341693"
1: Object
privacy: "private"
id: "803641223"
2: Object
privacy: "public"
id: "1300612600"
I also have a unique id I want to search for
var uniqueId = 803641223;
I want to find, in my videos array, the right id, and delete that whole array element. So In that case, I want my final videos array to contain only 2 object, instead of 3 :
var videos = $j.parseJSON('
[
{ "privacy":"public",
"id":"1169341693" },
{ "privacy":"public",
"id":"1300612600" }, ......
My problem is how to get in the array to do my splice. I prefer to do it with jQuery
Any help please?
You can use grep :
videos = $.grep(videos, function(e) { return e.id!='803641223' });
In vanilla JavaScript you could have used the similar filter function but it's not supported by IE8.
Please note that videos is a JavaScript array, it's not a JSON array, even if it was made by parsing a JSON string.
A non-jQuery solution that modifies the array in place:
var uniqueId = 803641223;
var videos = [
{ "privacy":"public",
"id":"1169341693" },
{ "privacy":"private",
"id":"803641223" },
{ "privacy":"public",
"id":"1300612600" }
];
function cleaner(arr, id) {
for (var i = 0; i < videos.length; i++) {
var cur = videos[i];
if (cur.id == uniqueId) {
arr.splice(i, 1);
break;
}
}
}
cleaner(videos, uniqueId);
http://jsfiddle.net/4JAww/1/
Note that this modifies the original array in place, such that the original videos array will have the items you want, and the one that matched the uniqueId will be gone (forever). So it depends on whether you want to be able to access the original array ever again, or are okay with modifying it.
It just loops through the elements of the array, compares the item's id property to the uniqueId value, and splices if they match. I use break; immediately after the splice because you seem to imply that the uniqueId can/should only appear once in the array since it's...unique.
Hello you can remove element with javascript splice function...
videos.items.splice(1, 3); // Removes three items starting with the 2nd,
It worker for me.
arrList = $.grep(arrList, function (e) {
if(e.add_task == addTask && e.worker_id == worker_id) {
return false;
} else {
return true;
}
});
It returns an array without that object.
Hope it helps.