Having trouble stopping duplicate from being added to array - javascript

I have added an voice command prompt to my small ecommerce application. When I command the ai to add a product to the list it adds it and when I try to add it again it doesn't. However, when I try to add a different item the command prompt denies it. I want it to work for all item. I have tried to look up different options including indexOf but that method isn't working either.
function voiceCommand(data) {
const products = new Products();
let alanBtnInstance = alanBtn({
top: '15px',
left: '15px',
onCommand: function (commandData) {
if (commandData.command === "opencart") {
products.openCart();
}
else if (commandData.command === "closecart") {
products.closeCart();
}
else if (commandData.command === "addItem") {
// get cart Items to compare to commandData.name
const cartItem = data
cartItem.forEach(item => {
return item.amount = 1
});
const item = cartItem.map(item => item)
.find(item => item.title.toLowerCase() === commandData.name.toLowerCase());
cart = [...cart, item]
function hasDuplicates(arr) {
return new Set(arr).size !== arr.length;
}
if (hasDuplicates(cart)) {
alanBtnInstance.playText(`${item.title} is already in cart`);
return
}
else {
const buttons = [...document.querySelectorAll('.cart-btn')]
buttonsDOM = buttons;
buttons.forEach(button => {
let id = button.dataset.id;
let inCart = cart.find(item => item.id === Number(id));
if (inCart) {
button.innerText = "In Cart";
button.disabled = true;
}
});
products.addCartItem(item);
products.setCartValues(cart);
products.openCart()
return
}
}
},
rootEl: document.getElementById("alan-btn"),
});
}

I simplified the code a little to show the basic principle (I hope I was not mistaken in the logic)
let cart = [];
const addItem = (data, commandData) => {
const item = data
.find(item => item.title.toLowerCase() ===
commandData.name.toLowerCase());
const dup = cart
.find(item => item.title.toLowerCase() ===
commandData.name.toLowerCase());
if (dup) {
//console.log(`${dup.title} is already in cart`)
dup.amount += 1; // just change amount
item.amount -= 1; // calc rest in store
} else {
cart = [...cart, {...item, amount: 1}] // insert new
item.amount -= 1; // calc rest in store
}
}
// store with amounts
const data = [
{id:'0', amount: '100', title: 'a'},
{id:'0', amount: '100', title: 'b'}
]
console.log('Cart before:')
console.log(JSON.stringify(cart))
console.log('Store before:')
console.log(JSON.stringify(data))
// test actions:
addItem(data, {name: 'b'})
addItem(data, {name: 'b'})
addItem(data, {name: 'a'})
console.log('Cart after:')
console.log(JSON.stringify(cart))
console.log('Store after:')
console.log(JSON.stringify(data))
I think your code should be like this:
else if (commandData.command === "addItem") {
// get cart Items to compare to commandData.name
const cartItem = data;
cartItem.forEach(item => {
return item.amount = 1
});
const item = cartItem.find(item => item.title.toLowerCase() === commandData.name.toLowerCase());
const dup = cart.find(item => item.title.toLowerCase() === commandData.name.toLowerCase());
if (dup) {
alanBtnInstance.playText(`${item.title} is already in cart`);
return
}
else {
cart = [...cart, item]
const buttons = [...document.querySelectorAll('.cart-btn')]
buttonsDOM = buttons;
buttons.forEach(button => {
let id = button.dataset.id;
let inCart = cart.find(item => item.id === Number(id));
if (inCart) {
button.innerText = "In Cart";
button.disabled = true;
}
});
products.addCartItem(item);
products.setCartValues(cart);
products.openCart()
return
}
}

Related

Is there a better way to achieve this?

I am using React. On click of a button, the following function is executed:
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const updatedData = [...prevData];
const updatedItem = updatedData.filter((ele) => ele.id === idValue)[0];
updatedItem.completed = true;
const newData = updatedData.filter((ele) => ele !== updatedItem);
newData.unshift(updatedItem);
return newData;
});
};
My data is an array of objects like this:
[{userId: 1, id: 2, title: "task 1", completed: true}, .....].
Basically I want to move the updated item to the start of the array. Is there any better solution for this?
updatedItem should not be mutated. And this string const newData = updatedData.filter((ele) => ele !== updatedItem); is not fine. You can do it like this :
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const targetItem = prevData.find((ele) => ele.id === idValue);
const updatedItem = { ...targetItem, completed: true };
const filteredData = prevData.filter((ele) => ele.id !== idValue);
return [updatedItem, ...filteredData];
});
};
Even better to reducing an extra filter:
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const targetIndex = prevData.findIndex((ele) => ele.id === idValue);
return [{ ...prevData[targetIndex], completed: true }].concat(prevData.slice(0, targetIndex + 1)) .concat(
prevData.slice(targetIndex + 1)
)
});
};
First find index of updated element using Array.findIndex(), then remove the same element using Array.splice() and add it to front of the array.
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const updatedData = [...prevData];
const index = updatedData.findIndex(obj => obj.id === idValue);
const [updatedItem] = updatedData.splice(index, 1);
updatedItem.completed = true;
updatedData.unshift(updatedItem);
return updatedData;
});
};
The simplest one with only one forEach.
const completeTaskHandler = idValue => {
setData(prevData => {
let updatedItem = {}, newData = [];
prevData.forEach((ele) => {
if (ele.id === idValue) {
updatedItem = ele;
updatedItem.completed = true;
} else {
newData.push(ele);
}
});
newData.unshift(updatedItem);
return newData;
});
};

How to remove an object from Array in React

I'm new to these stuff but I am passionate about learning it. So feel free to link documentations, I would gladly look up for them. I have built a cart list component in Reactjs. I implemented addToCart and removeFromCart functions. The problem lies in removeFromCart function. I have a json file from which I get my categories and products. I have onClick declerations that change the state of the component and render the new product list for the desired category. I added a button that removes products from cart but the button only decrease the quantity of the product. I want to remove the product when its quantity drops below zero. Here is my code, I hope you could help.
changeCategory = (category) => {
this.setState({ currentCategory: category.categoryName });
this.getProducts(category.id);
};
resetCategory = (category) => {
this.setState({currentCategory: "",});
this.getProducts()
};
getProducts = (categoryId) => {
let url = "http://localhost:3000/products";
if (categoryId) {
url += "?categoryId=" + categoryId;
}
fetch(url)
.then((response) => response.json())
.then((data) => this.setState({ products: data }));
};
addToCart = (product) => {
let newCart = this.state.cart;
var addedItem = newCart.find((c) => c.product.id === product.id);
if (addedItem) {
addedItem.quantity += 1;
} else {
newCart.push({ product: product, quantity: 1 });
}
this.setState({ cart: newCart });
alertify.success(product.productName + " added to the cart.,", 2);
};
This is what the states of component looks like:
state = {
currentCategory: "",
products: [],
cart: [],
};
And lastly the problematic part:
removeFromCart = (product) => {
let newCart = this.state.cart;
var addedItem = newCart.find((c) => c.product.id === product.id);
if (addedItem) {
addedItem.quantity -= 1;
}
// var zeroItem = addedItem.quantity ;
// zeroItem = newCart.filter((a) => a.addedItem.quantity !== 0)
// this.state.zeroItem.filter((c) => c.product.id !== product.id);
this.setState({ cart: newCart });
alertify.error(product.productName + " removed from the cart.", 2);
};
Comment section was what I tried but they didn't work, obviously. How can I remove a product in a project like this when its quantity drops below zero?
Thanks everyone in advance.
For those who would help: I have no problem for decreasing the quantity. I just need a way to remove that specific object it when the addedItem's quantity drops below 0.
You can simply use a filter to remove an item from the array.
removeFromCart = (product) => {
let newCart = this.state.cart;
var addedItem = newCart.find((c) => c.product.id === product.id);
if (addedItem && addedItem.quantity > 1) {
addedItem.quantity -= 1;
} else {
newCart = newCart.filter(({ id }) => id === product.id)
}
// var zeroItem = addedItem.quantity ;
// zeroItem = newCart.filter((a) => a.addedItem.quantity !== 0)
// this.state.zeroItem.filter((c) => c.product.id !== product.id);
this.setState({ cart: newCart });
alertify.error(product.productName + " removed from the cart.", 2);
};
I just edited your code a little and now it works like a charm. You can see the revised version of mine below.
removeFromCart = (product) => {
let newCart = this.state.cart;
var addedItem = newCart.find((c) => c.product.id === product.id);
if (addedItem && addedItem.quantity > 1) {
addedItem.quantity -= 1;
} else {
newCart = newCart.filter((c) => c.product.id !== product.id);
this.setState({ cart: newCart });
}
this.setState({ cart: newCart });
alertify.error(product.productName + " removed from the cart.", 2);
};
removeFromCart = (product) => {
let {cart}=this.state;
let {quantity:currentQuantity} = cart.find(pr => pr.product ===product)
if(currentQuantity > 1) {
this.setState(prevState =>
({cart : [...prevState.cart.filter(pr => pr.product !== product), {product, quantity: currentQuantity-1} ] }))
}
else { 
this.setState(prevState =>
({cart : prevState.cart.filter(pr => pr.product !== product) }))
}
}

When add item to local storage after refresh I lose others items

When I add an item after refresh the other items are lost and only the item that was added after the refresh of the page remains
My code:
let buttonsDom = document.querySelectorAll(elementsStr.itemsBtn)
const buttons = [... buttonsDom]
window.addEventListener('DOMContentLoaded', () => {
Storage.saveProducts(itemStr)
let cart = []
buttons.forEach((btn) => {
const id = btn.dataset.id;
if(localStorage.getItem('cart')) {
if(Storage.findCart(id)){
btn.innerText = 'in cart';
btn.disabled = true;
}
btn.addEventListener('click', () => {
btn.innerText = 'in cart';
btn.disabled = true;
const cartItem = { ...Storage.findProduct(id), amount:1 }
cart = [...cart, cartItem]
Storage.saveCart(cart)
})
})
})
export class Storage {
static saveProducts(products) {
localStorage.setItem("items", products);
}
static findProduct(id) {
const products = JSON.parse(localStorage.getItem("items"));
return products.find((item) => item._id === id);
}
static findCart(id) {
const product = JSON.parse(localStorage.getItem("cart"));
return product.find((item) => item._id === id);
}
static saveCart(cart) {
localStorage.setItem("cart", JSON.stringify(cart));
}
}
Maybe should you fill the cart in retrieving it from the localStorage no ?
let buttonsDom = document.querySelectorAll(elementsStr.itemsBtn)
const buttons = [... buttonsDom]
window.addEventListener('DOMContentLoaded', () => {
Storage.saveProducts(itemStr)
let cart = Storage.retrieveCart(); // <---- Here
buttons.forEach((btn) => {
const id = btn.dataset.id;
if(localStorage.getItem('cart')) {
if(Storage.findCart(id)){
btn.innerText = 'in cart';
btn.disabled = true;
}
btn.addEventListener('click', () => {
btn.innerText = 'in cart';
btn.disabled = true;
const cartItem = { ...Storage.findProduct(id), amount:1 }
cart = [...cart, cartItem]
Storage.saveCart(cart)
})
})
})
export class Storage {
static saveProducts(products) {
localStorage.setItem("items", products);
}
static findProduct(id) {
const products = JSON.parse(localStorage.getItem("items"));
return products.find((item) => item._id === id);
}
static findCart(id) {
const product = JSON.parse(localStorage.getItem("cart"));
return product.find((item) => item._id === id);
}
static saveCart(cart) {
localStorage.setItem("cart", JSON.stringify(cart));
}
static retrieveCart() { // <---- Here
let cart = localStorage.getItem("cart");
if (cart === null) {
return [];
}
try {
return JSON.parse(cart);
} catch(e) {
return [];
}
}
}

How to increase quantity of the item if it is already added in shopping cart in React

I am facing an issue with shopping cart. Unable to increase quantity of existing item in cart or add another item. On button click the addToCart function executes which takes a product.
const [cartItems, setCartItems] = useState([])
const addToCart = product => {
console.log(product) // here I am getting the entire product
const index = cartItems.findIndex(item => item._id === product._id);
if (index === -1) {
const updateCart = cartItems.concat({
...product,
quantity: 1
});
setCartItems(updateCart);
} else {
const updateCart = [...cartItems];
updateCart[index].quantity += 1;
setCartItems(updateCart);
}
};
I am getting only 1 quantity of product and if I add another product or increase quantity, it overwrites.
Your else logic is wrong. You want to update your item quantity from carItems, before you spread it.
Change:
const updateCart = [...cartItems];
updateCart[index].quantity += 1;
setCartItems(updateCart);
to
cartItems[index].quantity += 1;
const updateCart = [...cartItems];
setCartItems(updateCart);
Edit: See it in action below:
let cartItems = [{
_id: "1",
name: "shoe",
quantity: 1
}];
const addToCart = product => {
console.log(product) // here I am getting the entire product
const index = cartItems.findIndex(item => item._id === product._id);
if (index === -1) {
cartItems.push({
...product,
quantity: 1
});
const updateCart = [...cartItems];
console.log(updateCart);
} else {
cartItems[index].quantity += 1;
const updateCart = [...cartItems];
console.log(updateCart);
}
};
var product1 = {
_id: "1",
name: "shoe"
};
var product2 = {
_id: "2",
name: "apple"
};
addToCart(product1);
addToCart(product2);
Your code should work, is quite good
probably testing method failed
problems were related to different parts of code
let cartItems = [
{
_id: "1",
name: "shoe",
quantity: 1
}
];
console.log("START", JSON.stringify(cartItems), Array.isArray(cartItems));
const addToCart = product => {
console.log(product); // here I am getting the entire product
const index = cartItems.findIndex(item => item._id === product._id);
if (index === -1) {
const updateCart = cartItems.concat({
...product,
quantity: 1
});
cartItems = updateCart;
console.log("ADD", JSON.stringify(cartItems), Array.isArray(cartItems));
} else {
// original ... just works ...
// const updateCart = [...cartItems];
// updateCart[index].quantity += 1;
// ... this works, too
// cartItems[index].quantity += 1;
// const updateCart = [...cartItems];
// ... but this is safer (in react) as it replaces item with a new object
const updateCart = [...cartItems];
// new object for replace existing one
updateCart[index] = {
...updateCart[index],
quantity: updateCart[index].quantity + 1
};
cartItems = updateCart;
console.log("INC", JSON.stringify(cartItems), Array.isArray(cartItems));
}
};
var product1 = {
_id: "1",
name: "shoe"
};
var product2 = {
_id: "2",
name: "apple"
};
addToCart(product1);
addToCart(product2);
addToCart(product1);
console.log("END", JSON.stringify(cartItems), Array.isArray(cartItems));
working example
Is good enough? Not for react
Replacing item [-line] in cart with a new object can be required in react when you're rendering cart using components. f.e.:
cartItems.map( item => <CartOrderLine key={item._id} data={item} /> )
<CartOrderLine /> with the same data object ref (mutated quantity prop only, not replaced) won't be rerendered!

ReactJs functions that need refractoring (if possible)

I have a few functions I've written for a basic e-commerce store, but both seem a little lengthy to me.
Perhaps someone out there has some suggestions on how I might be able to refractor them?
Cheers.
1 - a function that had adds an item to the cart or wishlist based on an action:
addToFunnelHandler = (uuid, price, title, action) => {
let productToAdd = {}
productToAdd.uuid = uuid
productToAdd.price = price
productToAdd.title = title
if (action === 'bag') {
let productsInBag = [...this.state.productsInBag]
productsInBag.push(productToAdd)
this.setState({ productsInBag: productsInBag, bagCount: this.state.bagCount + 1, bagTotal: this.state.bagTotal + price })
} else if (action === 'wishlist') {
let productsInWishlist = [...this.state.productsInWishlist]
productsInWishlist.push(productToAdd)
this.setState({ productsInWishlist: productsInWishlist, wishlistCount: this.state.wishlistCount + 1})
}
}
2 - Same but in reverse, removing an item from the funnel:
removeFromFunnelHandler = (uuid, price, action) => {
if (action === 'bag') {
let productsInBag = [...this.state.productsInBag]
productsInBag.forEach((product, index) => {
if (product.uuid === uuid) { productsInBag.splice(index, 1) }
})
this.setState({ productsInBag: productsInBag, bagCount: this.state.bagCount - 1, bagTotal: this.state.bagTotal - price })
} else if (action === 'wishlist') {
let productsInWishlist = [...this.state.productsInWishlist]
productsInWishlist.forEach( (product, index) => {
if (product.uuid === uuid) { productsInWishlist.splice(index, 1) }
})
this.setState({ productsInWishlist: productsInWishlist, wishlistCount: this.state.wishlistCount - 1 })
}
}
As for the first function, you could remove most of the declarations:
Declare productToAdd in one go
Rather than pushing, just include the value in the array
addToFunnelHandler = (uuid, price, title, action) => {
let productToAdd = { uuid, price, title }
if (action === 'bag') {
this.setState({ productsInBag: [...this.state.productsInBag, productToAdd], bagCount: this.state.bagCount + 1, bagTotal: this.state.bagTotal + price })
} else if (action === 'wishlist') {
this.setState({ productsInWishlist: [...this.state.productsInWishlist, productToAdd], wishlistCount: this.state.wishlistCount + 1 })
}
}
For the second, there's not much to refactor without more context:
Use inline if statements
Use shorthand property assiging
Use filter over forEach
removeFromFunnelHandler = (uuid, price, action) => {
if (action === 'bag') {
const productsInBag = [...this.state.productsInBag].filter(product => product.uuid !== uuid)
this.setState({ productsInBag, bagCount: this.state.bagCount - 1, bagTotal: this.state.bagTotal - price })
} else if (action === 'wishlist') {
const productsInWishlist = [...this.state.productsInWishlist].filter(product => product.uuid !== uuid)
this.setState({ productsInWishlist, wishlistCount: this.state.wishlistCount - 1 })
}
}

Categories

Resources