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.
Related
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;
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.
I am creating a javascript cart.
Here's how I add a product to the cart:
function addToCart(id, qty){
var basket = []
basket.push({
product_id: id,
quantity: qty
});
localStorage.setItem('basket', JSON.stringify(basket));
}
Now if id is already there, I am trying only to update current basket from localStorage
When I add the same product id, it's duplicating it.
[{"product_id":"10", "quantity":"1"}, {"product_id":"10", "quantity":"1"}]
I want to increase only quantity like:
[{"product_id":"10", "quantity":"2"}]
Tried few methods with each and for with no luck :(
Any help please?
You're replacing your stored array every time, which won't have the problem you've described unless you don't really have the var basket = [] in your function. But the problem you've described would be caused by not checking for an existing entry with the product ID.
Instead:
Keep your array in memory, not just local storage
Load it from local storage on page load
Update it in local storage when you change your memory copy
Remove it from local storage when the user completes their purchase or clears their basket
For #1 and #2: In a place that's global to your code (but ideally not really global):
var basket = JSON.parse(localStorage.getItem("basket")) || [];
For #3:
function rememberBasket() {
localStorage.setItem('basket', JSON.stringify(basket));
}
function addToCart(id, qty){
// See if product exists
var entry = basket.find(function(e) { return e.product_id == id; });
if (entry) {
entry.quantity += qty; // Or just `= qty` to replace rather than adding
} else {
basket.push({
product_id: id,
quantity: qty
});
}
rememberBasket();
}
For #4, of course:
basket = [];
rememberBasket();
Your original code was all ES5 and earlier, so I stuck to that above, but ES2015+ features would make it more concise.
Solution :
function addToCart(id, qty) {
var newItem = true;
var basket = json.parse(localStorage.getItem('basket'));
basket.forEach(function (item){
if(item.product_id == id) {
item.quantity += qty;
newItem = false;
}
})
if(newItem) {
basket.push({
product_id: id,
quantity: qty
});
localStorage.setItem('basket', JSON.stringify(basket));
}
}
First, you need to try to read the basket out of localStorage instead of starting with an empty array each time. Second, I'd recommend you use an object instead of an array. Your products already have ids, so instead of searching the array each time, just let the language do the key lookup for you. Last, you're missing any treatment of how to update existing items in the cart. Here's how I'd approach that, supporting both adding new items to the cart, and increasing the quantity of existing items.
function addToCart(id, qty){
var basket = JSON.parse(localStorage.getItem('basket')) || {};
if (basket[id]) {
basket[id] += qty;
} else {
basket[id] = qty;
}
localStorage.setItem('basket', JSON.stringify(basket));
console.log(basket);
}
addToCart(1,1);
addToCart(2,1);
addToCart(1,1);
addToCart(3,2);
addToCart(3,1);
// results in:
VM222:9 {1: 1}
VM222:9 {1: 1, 2: 1}
VM222:9 {1: 2, 2: 1}
VM222:9 {1: 2, 2: 1, 3: 2}
VM222:9 {1: 2, 2: 1, 3: 3}
Try this
function addToCart(id, qty){
var basket = JSON.parse(localStorage.getItem('basket'));
var isExists = false;
if(basket === null || basket === undefined){
basket = [];
}
basket.reduce(function(o,i){
if(i.product_id === id){
i.quantity += qty;isExists = true;
}
o.push(i);
return o;
},[]);
if(!isExists){
basket.push({
product_id: id,
quantity: qty
});}
localStorage.setItem('basket', JSON.stringify(basket));
}
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.
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