Check array elements and if there isn't add to it - javascript

I have an array that is at the beginning empty, so I have my function that I use to controll element inside array:
saveProducts(product){
let originalProd = JSON.parse(localStorage.getItem("PRODUCT_ORIGINAL")) // at the beginning is null
if( originalProd !== null && originalProd.length > 0){
originalProd.some(element => {
if ( element.productName !== product.name){
this.originalProductToSave.push({ productName: product.name, productQuantity: product.quantity, productPrice: product.price })
else if(element.productName === product.name && element.productQuantity !== product.quantity){ element.productQuantity = product.quantity}
}
}
}
else{
this.originalProductToSave.push({ productName: product.name, productQuantity: product.quantity, productPrice: product.price })
}
}
Now this originalProductToSave is saved on localStorage in this way originalProd has the same values.
My problem is about how to save elements inside array because I don't want duplicate, and using some I have duplicated inside, for example
originalProd=[{productName: "001",....}, {productName: "002",.....}]
So If I try for example to add another time product with name 002 it checks between 001 != 002 and it adds another time 002

So what you are looking for is adding an element to an array of objects on the condition that the array does not hold an object already which has the same value in its productName just like product.name , correct?
If so, there are multiple ways to solve this elegantly i think. For instance you could try JS' in-built find method (see here for docs) and try looking for an already existing entry like that:
// "deep checking" the objects for equality
let find = originalProd.find(elem =>
elem.productName === product.name
&& elem.productQuantity === product.quantity
&& elem.productPrice === product.price);
if (!find) {
// element is not contained yet, so add it
// ...
}
Another elegant solution could be by just simply using a map instead of an array for storing your elements and using the in-built has method on maps (see here for docs).
Apart from that, you may find this related post helpful.
Hope this helps!

Related

Can't get mutator logic to correctly add the proper amount of items into a cart

I'm trying to make an add to cart function that first checks if the item being added is already in the cart. If it's in the cart, update its quantity property. If not in the cart, add the entire object to the cart. I think my problem is I'm getting the logic wrong inside my "ADD_ITEM_TO_CART" mutator function.
This is my store with some console.logs() from when I click "addToCart()"
state: {
checkoutCart: [],
},
actions: {
cartAdd({ commit }, payload) {
commit("ADD_ITEM_TO_CART", payload);
},
},
mutations: {
ADD_ITEM_TO_CART(state, payload) {
//CONSOLE.LOG()'s
console.log("state.checkoutCart[0]", state.checkoutCart[0]);
// eslint-disable-next-line
console.log("state.checkoutCart[0].item", state.checkoutCart.item);
console.log("state.checkoutCart", state.checkoutCart);
//IF ITEM ALREADY IN checkoutCart, UPDATE IT'S QUANTITY
if (state.checkoutCart.includes(payload.item)) {
state.checkoutCart.quantity += payload.quantity;
console.log("Item already in cart");
}
//IF ITEM NOT IN checkoutCart, UPDATE THE QUANTITY PROPERTY AND ADD ITEM TO CART
else {
payload.item.quantity = payload.quantity;
state.checkoutCart.push(payload);
}
https://i.imgur.com/rjOOljN.png
I thought this code would work, but it ALWAYS executes the ELSE condition and adds to cart like the
if (state.checkoutCart.includes(payload.item))
isn't being recognized or working at all.
https://i.imgur.com/LLB790Z.png
VueX devtools shows the same thing. An "item" object inside an object inside an array.
I also tried:
ADD_ITEM_TO_CART(state, payload) {
console.log("add_item_to_cart"); <---ONLY PART THAT SHOWS UP IN CONSOLE.LOG() WHEN EXECUTED
//LOOP THROUGH ALL ARRAY ENTRIES TO GAIN ACCESS TO state.checkoutCart.item
for (let i = 0; i < state.checkoutCart.length; i++) {
console.log("i=", i);
console.log("state.checkoutCart.item", state.checkoutCart.item);
//IF ITEM ALREADY IN checkoutCart, UPDATE IT'S QUANTITY
if (state.checkoutCart[i].item.includes(payload.item)) {
state.checkoutCart.quantity += payload.quantity;
console.log("Item already in cart");
return;
};
}
//IF ITEM NOT IN checkoutCart, UPDATE THE QUANTITY PROPERTY AND ADD ITEM TO CART
payload.item.quantity = payload.quantity;
state.checkoutCart.push(payload);
},
because I figured I needed to loop through all the array entries. BUT the for loop doesn't even run, and with this code nothing gets added to the cart at all.
I can't figure out what I'm doing wrong here. Can somebody help? Is my syntax wrong? Or is my logic? Am I accessing the arrays/objects incorrectly? How do I write the "ADD_ITEM_TO_CART" mutator function correctly? I've literally spent all day on this and my brain is shutting down.
EDIT:
https://i.imgur.com/bkU8YSo.png
PAYLOAD
<div v-for="item in items"> <--ACTUALLY PROP FROM PARENT COMPONENT BUT SAME IDEA
<p>
Qty
<select v-model="quantity">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</p>
<p>
<button type="button" #click="addToCart()">
Add to Cart
</button>
</p>
</div>
let quantity = ref("1");
const addToCart = () => {
console.log("addToCart Running");
store.dispatch("cartAdd", { item: item.value, quantity: quantity.value });
};
That is because your if condition is not checking for what you think.
Array.prototype.includes checks if a value is in the array but there are two cases:
the value is a primary type (string, number, boolean, ...). It compares by value.
the value is an object. Then it compares by reference.
So here, you are checking if the reference of your item object is already included in the array. But it's not, since it's a new object.
Solution: check if there is an object with the same values, not reference.
You can use the some method, and you have to write a condition that checks if two items are equals.
Here is an example if your items have an id:
if (state.checkoutCart.some(item => item.id === payload.item.id))
The problem is indeed inside ADD_ITEM_TO_CART mutation.
As Kapcash has pointed out, two objects having the same properties and the same values are not the same.
In other words, .includes() checks for identity, not equality. To better understand this, consider this example:
const a = { foo: 'bar' }
const b = [{ foo: 'bar' }]
const c = [a]
const d = [{ ...a }]
console.log(b.includes(a)) // false
console.log(c.includes(a)) // true
console.log(d.includes(a)) // false
To get past this, use Kapcash's answer.
I'll just mention the standard way of dealing with this problem is using unique identifiers on objects (e.g: uuids).
Once you fix the above, it's still not going to work, because you'll run into the following problem: inside the if you're attempting to alter state.checkoutCart's quantity. And an array does not have a quantity property.
The proper logic to achieve the desired functionality is (assuming the unique identifier on checkoutCart members is item._id, from the pictures you posted):
ADD_ITEM_TO_CART(state, payload) {
// find the index of a cart element having same `item._id` value as the payload
const index = state.checkoutCart.findIndex(
(el) => el.item._id === payload.item._id
)
if (index > -1) {
// if the index is higher than `-1`, an element was found
// create a copy, update its quantity and then
// replace the original element with the copy
const copy = { ...state.checkoutChart[index] }
copy.quantity += payload.quantity
state.checkoutCart.splice(index, 1, copy)
} else {
// no element found, add payload to `checkoutCart`
state.checkoutCart.push(payload)
}
}
Side note: None of your items should contain a quantity property. That's the cart's responsibility, not the item's. By adding it to the item you end up with two sources of truth which you'd need to keep in sync. You don't want this type of problem.

Assign new name to object's key in an array of objects

In a Grid, records are fetched from API and displayed. It also has certain input fields, with text and date field. While inputting data for date its getting displayed two times, as seen in the console, the data from JSON is as est: 10/20/2022 but I want to display it as Establish: 10/20/2022. What modifications could be made in the code? Please refer to code below.
//Here 'allData' is an array of data from JSON
const tempData = allData;
tempData.map((x) => {
if (data.id === x.id) {
x.name = data.textVal;
}
// Here I'm trying to assign new key 'Establish' to old key 'est'
if (data.id === x.id) {
x["est"] = x["Establish"];
x.Establish = data.dateVal;
}
});
Please refer to codesandbox link --> https://codesandbox.io/s/jovial-aryabhata-95o2sy?file=/src/Table.js
Give this a whirl
if(data.id === x.id) {
delete Object.assign(x, {['Establish']: x['est'] })['est'];
}
I think your way works but if you want another way so you can create a new property by using:
Object.defineProperty(x, "Establish", Object.getOwnPropertyDescriptor(x, "est"));

Set state based on value of array item in previous state

Hello I am new to programming and I am trying to make a function in React that adds a team to an array of selected teams but only if there are less than 2 teams selected and if the team is available (not already chosen). It is working to limit the array to only 2 values but the array will accept the same team twice and I am not sure why. For example if the user clicks ATL twice then that team will be in the array twice. What did I do wrong here and what should I change in order to fix it? Sorry if the question is too simple for this forum, I am new.
Here is the code where I am changing the state and checking if gameState.selected_teams[0] != team:
function App() {
const [gameState, setGameState] = useState({
cards: Cards,
selected_teams: []
});
function setTeam(team){
if (gameState.selected_teams.length < 2 && gameState.selected_teams[0] != team) {
setGameState((prevState) => ({
cards: prevState.cards,
selected_teams: [
...prevState.selected_teams, {
team
}
]
}))
}
}
And for the component that calls the setTeam function:
function TeamSelect({cards, setTeam}) {
var teams = Object.keys(cards);
return (
<div className='flex-container'>
<div className='team-container'>
{
teams.map(team => (
<div
onClick={() => setTeam(team)}
className='team-label' key={team}>
{team}
</div>
))
}
</div>
</div>
)
}
You're adding an object {team: team} to your selected teams array each time you perform your click here:
selected_teams: [
...prevState.selected_teams, {
team
}
]
but your team key that you pass into your setTeam function is a string, so your comparison fails as you're trying to compare a string with an object. You can change your comparison to extract the team property from your object:
gameState.selected_teams[0]?.team != team
The ? ensures that a value exists at index 0 in your array before using .team on it (otherwise you would get an error if it's undefined).
You can adapt this code to handle more than one object by using .every() to check that all objects in selected_team's aren't equal to the one you're adding:
if(gameState.selected_teams.length < 2 && gameState.selected_teams.every(obj => obj.team != team)
If you don't need to pass an object {team: team} (as an object with one property doesn't add much value), then you can simply push your team string into your selected teams, and use .includes() to check if the team you're adding already exists in the array:
selected_teams: [
...prevState.selected_teams, team
]
you can then update your condition to use .includes():
if(gameState.selected_teams.length < 2 && !gameState.selected_teams.includes(team))

Check Javascript nested obj value exists with unknown key

The following is used to build up a object array:
var users = {};
var user = {};
user[socket.id] = data.username;
if(users[data.roomname]){
// Room already exists - check user already exists
// if data.username does not value exist is users then:
users[data.roomname].push(user);
}
else{
// New room
users[data.roomname] = [user];
}
Over a few iterations we get something like this:
console.log ( 'Users: ', users );
users { RoomABC:
[ { YidwzgUHPHEGkQIPAAAD: 'Mr Chipps' },
{ 'JG-gtBMyPm0C1Hi1AAAF': 'Mr T' },
{ '2JFGMEdPbgjTgLGVAAAH': 'Mr Chipps' }, ] }
The issue is trying to ensure that each username is unique, so Mr Chipps should not be added again if that name already exists.
The examples I have seen Assume the keys are known. I have tried a number of things including some, indexOf but I am not able to get a simple 'does UserX already exist' to work.
The following is the latest block of code I tried to only add the user if not already present in the obj array. This works, but it seems very clunky to me; nested loops to get at the correct level to check the value and set a counter if a match found, then check the counter to decide if a match was found or not:
if(users[data.roomname]){
// Room already exists - check user already exists
let found = 0;
// Nested loop - seems a little clunky but it works
Object.keys(users[data.roomname]).forEach(key => {
Object.keys(users[data.roomname][key]).forEach(key2 => {
if ( users[data.roomname][key][key2] === data.username ) {
found++;
}
});
});
if ( found == 0 ) {
users[data.roomname].push(user);
}
}
I keep thinking surely there is neat one-liner that can do this check for the existence but I cant get any to work.
You could check the values instead of using the keys and exit early if a name is found
if (users[data.roomname]) {
if (!Object.values(users[data.roomname]).some(v => Object.values(v).some(n => n === data.username))) {
users[data.roomname].push(user);
}
}

Why does comparing a property to the author.id not work?

So here is my current code for a currency system. This code works to add the new user information in. Obviously this will keep adding the people that are already in it.
if (!currency[message.author.id]) {
currency.push({id: message.author.id, coins: 0});
}
if I change it to this one, nothing happens. It seems there's something wrong with this comparison and I'm not sure what it is considering it worked for other things I have used.
if (!currency[0].id == message.author.id) {
currency.push({id: message.author.id, coins: 0});
}
This looks right to me as it's getting the id property of the first element and checking if they're the same. When I run the code it just doesn't do anything. No errors and nothing in the json file I'm using to store it. It does this when the array is empty and does it when I have an id property in there.
Is this not possible? I don't like having to set it up using the first way because I'd like to be able to access everyone's currency if needed to add or take away without having to do it one person at a time.
I think it should be !==:
if (currency[0].id !== message.author.id) {
currency.push({id: message.author.id, coins: 0});
}
or should be wrapped in brackets:
if (!(currency[0].id == message.author.id)) {
currency.push({id: message.author.id, coins: 0});
}
It sounds like you probably want a data structure that's an object, not an array, so that you can have arbitrary key-value pairs - have the key be the message.author.id.
const currency = {};
// ...
const { id } = message.author;
if (!currency[id]) {
currency[id] = { id, coins: 0 };
} else {
// this author was already inserted - do something else here, if desired
// current[id].coins += coinChangeAmount; // for example
}
Your original code sounds like it's misusing an array as an object, and the currency[0].id == message.author.id will only check the [0]th element of the array, rather than iterating over all possible elements and looking for an ID match.

Categories

Resources