My state looks like so:
items: [
{ id: 1, name: 'banana', price: 100, quantity: 1 },
{ id: 2, name: 'apple', price: 200, quantity: 1 },
{ id: 3, name: 'blueberry', price: 300, quantity: 1 }
]
cart: []
I have a function where I push the item to the cart:
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const i = cart.indexOf(item)
if (!cart.some(x => x.id === id)) {
this.setState({
cart: [...cart, { ...item, quantity: itemQuantity }],
total: total + (price * itemQuantity)
})
}
}
I'm checking to see if the item exists before adding it to avoid duplicates. What I want to happen is if the item is already added to the cart, I want to find that object and change its quantity value.
Is this possible to do?
I think the solution below would work for you without having to update your design much. You really just have to call Array.prototype method reduce on an array of items you wish to add to the cart. Make sure to pass your cart's current state as the initial value to reduce (pass it as the second argument). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
With some simple logic, you could just check to see if the item id already exists in the cart (which is just an object literal keyed by item id). If it doesn't, add the object and set the quantity property to 1 - if it does, just increment the quantity property by 1.
Hopefully the example below is clear and can help you out:
//an array of items we want to add to the cart
var items = [{
id: 1,
name: 'banana',
price: 100,
quantity: 1
},
{
id: 2,
name: 'apple',
price: 200,
quantity: 1
},
{
id: 3,
name: 'blueberry',
price: 300,
quantity: 1
}
];
//function utilizing reduce to update our cart object
function updateCart(cartToUpdate, itemsToAdd) {
itemsToAdd.reduce(function(cart, cartItem) {
if (!cart[cartItem.id]) {
cart[cartItem.id] = {
name: cartItem.name,
price: cartItem.price,
quantity: 1
}
} else {
cart[cartItem.id].quantity++;
}
return cart;
}, cart);
}
//implement cart as an object keyed on item id
var cart = {
1: {
name: 'banana',
price: 100,
quantity: 1
}
};
//i manually added a single banana to the cart so we can show that our update function works properly
console.log('Cart before update:');
console.log(cart);
updateCart(cart, items);
console.log('Cart after update:');
console.log(cart);
//from here it's really easy to see how many of each item we have in the cart:
Object.keys(cart).forEach(function(key) {
console.log(`The customer has ${cart[key].quantity} ${cart[key].name}(s) in their cart`)
});
//
You could do something like this:
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const index = cart.findIndex(x => x.id === id);
let quantity = itemQuantity;
const newCart = index === -1
? [...cart, {...item, quantity }]
: cart.map((it, i) => {
if (i === index) {
quantity = it.quantity + quantity;
return { ...it, quantity }
} else return it
});
this.setState({
cart: newCart,
total: total + (price * quantity)
});
}
Yes, just leverage the i const that you've already defined!
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const i = cart.indexOf(item)
const newCart = [...cart]
const newTotal = total + (price * itemQuantity)
if (i !== -1) {
const newItem = { ...item, quantity: itemQuantity + 1 }
newCart[i] = newItem
this.setState({
cart: newCart,
total: newTotal,
})
return
}
newCart.push({ ...item, quantity: itemQuantity })
this.setState({
cart: newCart,
total: newTotal,
})
}
Note, it was unclear how you wanted the total functionality to work, so I've left that as it is. This, however will update the item you're looking for.
I had to check to see whether the item was added to the cart before pushing a new object with that item.
I then had to find the index of that item in the cart and update its quantity.
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const i = cart.findIndex(x => x.id === id)
if (!cart.some(x => x.id === id)) {
this.setState({
cart: [
...cart,
{ ...item, quantity: itemQuantity }
]
})
} else {
this.setState({
cart: [
...cart.slice(0, i),
{ ...cart[i], quantity: cart[i].quantity + itemQuantity },
...cart.slice(i + 1)
]
})
}
this.setState({
total: total + (price * itemQuantity)
})
}
Related
I tried to create a counter which is included in a mapped object so each time i click on the increment buttons it increase all the four counters at once meanwhile i want to use only three states: the counter, increase button and decrease button
const [details, setDetails] = useState([
{
picture: Lantern,
bookTitle: 'Quantitative Reasoning',
school: 'Primary School',
counter: 0,
id: 1,
increseBtn: 1,
},
{
picture: Lantern,
bookTitle: 'Quantitative Reasoning',
school: 'Primary School',
decrecreseBtn: '-',
increseBtn: 2,
price: 0,
remove: 'Remove',
id: 2
},
{
picture: Lantern,
bookTitle: 'Quantitative Reasoning',
school: 'Primary School',
decrecreseBtn: '-',
increseBtn:3,
remove: 'Remove',
id: 3,
counter: 0,
},
{
picture: Lantern,
bookTitle: 'Quantitative Reasoning',
school: 'Primary School',
decrecreseBtn: '-',
increseBtn: 4,
remove: 'Remove',
counter: 0,
id: 4,
// increseBtn: {increase},
},
]
)
const increment = (id) => {
setcounter(counter+1)
}
const decrement = () => {
setcounter(counter-1)
}
<button onClick={decrement}>-</button>
<small>{counter} </small>
<button onClick={()=> increment(add.id)}>+</button>
You can update by passing an id to the increment and decrement function
const increment = (id) => {
setDetails(details.map(item => {
if (item.id === id) {
// Create a *new* object with changes
return { ...item, counter: (item.counter ?? 0) + 1 };
} else {
// No changes
return item;
}
});
}
const decrement = (id) => {
setDetails(details.map(item => {
if (item.id === id) {
// Create a *new* object with changes
return { ...item, counter: (item.counter ?? 0) - 1 };
} else {
// No changes
return item;
}
});
}
// in the markup
<button onClick={() => decrement(add.id)}>-</button>
<small>{add.counter} </small>
<button onClick={()=> increment(add.id)}>+</button>
You can read more about working with arrays in React
Hope it helps
Just set the counter for the objects where id matches after each increment and decrement operation.
details = details.map(obj => {
if(obj.id === id) {
obj = {
...obj,
counter
}
}
retrun obj;
})
I have an array, that looks like this(size changes):
[
{ '385090261019131915': 34 },
{ '746430449240375297': 2 },
{ '810189312175374408': 1 },
{ '830832432680009789': 8 },
{ '850073735272988692': 1 }
]
The first value is the member id, the second how many messages the user has.
How can I sort the array, to get the first 10 members, sorted by their messages send?
The code:
if(command === 'leaderboard'){
const list = []
fs.readdirSync('./db/user/messages').forEach(file => {
const user = JSON.parse(fs.readFileSync(`./db/user/messages/${file}` , 'utf-8'))
userid = file.replace('.json','');
const entry = {[userid] : user.userall}
list.push(entry)
})
}
To sort an array by numbers, you can use the .sort() method with a compare function that subtracts the second value from the first one:
const arr = [34, 2, 1, 8, 1]
const sorted = arr.sort((a, b) => b - a)
console.log({ sorted })
As you're using objects, you should sort by an object key, but you're using the user ID as the key, so you don't know them. You can, however, get the value using the [Object.values()][2] method to get the value(s) and sort by them:
const arr = [
{ '385090261019131915': 34 },
{ '746430449240375297': 2 },
{ '810189312175374408': 1 },
{ '830832432680009789': 8 },
{ '850073735272988692': 1 }
]
const sorted = arr.sort((a, b) => Object.values(b)[0] - Object.values(a)[0])
console.log({ sorted })
Don't forget that Object.values() returns an array so you'll need to compare the first element.
However, instead of using the user ID as the key and the points as the value, I'd use two different keys in the object, one for the ID and one for the score:
const list = [
{ id: '385090261019131915', score: 34 },
{ id: '746430449240375297', score: 2 },
{ id: '810189312175374408', score: 1 },
{ id: '830832432680009789', score: 8 },
{ id: '850073735272988692', score: 1 }
]
const sortedList = list.sort((a, b) => b.score - a.score)
console.log({ sortedList })
And the final code:
if (command === 'leaderboard') {
const list = []
fs.readdirSync('./db/user/messages').forEach((file) => {
const user = JSON.parse(
fs.readFileSync(`./db/user/messages/${file}`, 'utf-8'),
)
const userId = file.replace('.json', '')
list.push({ id: userId, score: user.userall })
});
// sort by score
const sortedList = list.sort((a, b) => b.score - a.score)
}
I have an array of objects named Indicators and an Array named Departments
For every indicator I’m supposed to add each department. So if there are 4 departments, there should be 4 indicators with the same index but with different departments. How can I achieve this?
EDIT:
Here is my code
let arrDepartment = []
this.DepartmentIR.forEach(e=>{
arrDepartment.push(e.Department)
})
this.RisksIndicatorsIR.forEach(e=>{
let x = {...e}
x.Departments = arrDepartment
console.log(x)
})
})
const departments = [
"department1",
"department2",
"department3",
"department4"
];
const indicators = [
{ name: "indicators1", id: 95 },
{ name: "indicators2", id: 92 },
{ name: "indicators3", id: 93 },
{ name: "indicators4", id: 94 }
];
// Solution with forEach - all departments as key on each indicator
const newIndicators = [];
indicators.forEach((x) =>
newIndicators.push({ ...x, departments: departments })
);
console.log("Solution with forEach", newIndicators);
// Solution with Reduce - all departments as key on each indicator
const newIndicatorsArr = indicators.reduce((acc, x) => {
acc.push({ ...x, departments: departments });
return acc;
}, []);
console.log("Solution with Reduce", newIndicatorsArr);
// Solution if you want 1 department per indicator
const oneDepartmentPerIndicator = indicators.reduce((acc, x, i) => {
acc.push({ ...x, departments: departments[i] });
return acc;
}, []);
console.log("Solution with 1 department per indicator", newIndicatorsArr);
I want to sum the values of an array but I get an error with NaN
could you help me please?
const products = [
{
id: 1,
title: 'T-Shirt',
price: 98,
quantity: 10
},
{
id: 2,
title: 'Shoes',
price: 70,
quantity: 17
}
]
let cartItems = []
let parsIntPrice
let totalPrice = 0
const shoppingCart = (myId) => {
localStorage.setItem('myProduct', JSON.stringify(products))
const getStorage = localStorage.getItem('myProduct')
const finalProducts = JSON.parse(getStorage)
// SHOW EACH ITEM SELECTED
let selectedResult = finalProducts.filter(item => item.id === myId)
for (let product in selectedResult) {
console.log(selectedResult[product].title)
// CALCULATE TOTAL PRICES
parsIntPrice = selectedResult[product].price
totalPrice = cartItems.reduce((firstItem, secondItem) => firstItem + secondItem.parsIntPrice, 0)
console.log(`Total Price is: ${totalPrice}`)
}
}
Your cartItems array is empty, so reduce doesn't iterate it
I have checked two checkboxes. I can unchecked them, but i can't again checked them. When unchecked checkboxes, the value is removed from the array peopleChecked, when them wants to mark the value is added to the array peopleChecked, but the checkboxes aren't checked
Code here:
class App extends Component {
constructor() {
super();
this.state = {
people: [
{
firstname: "Paul",
userCompetences: [
{
asset: {
id: "12345"
}
}
]
},
{
firstname: "Victor",
userCompetences: [
{
asset: {
id: "5646535"
}
}
]
},
{
firstname: "Martin",
userCompetences: [
{
asset: {
id: "097867575675"
}
}
]
},
{
firstname: "Gregor",
userCompetences: [
{
asset: {
id: "67890"
}
}
]
}
],
peopleChecked: [
{
amount: 0,
asset: {
id: "fgfgfgfg",
name: 'Gregor'
},
asset_id: '67890'
},
{
amount: 0,
asset: {
id: "dsdsdsd"
},
asset_id: '12345'
},
],
selectPeopleId: []
}
}
handleSelect = (person) => {
//Check if clicked checkbox is already selected
var found = this.state.peopleChecked.find((element) => {
return element.asset_id === person.userCompetences[0]['asset']['id'];
});
console.log(found);
if(found){
//If clicked checkbox already selected then remove that from peopleChecked array
this.setState({
peopleChecked: this.state.peopleChecked.filter(element => element.asset_id !== person.userCompetences[0]['asset']['id']),
selectPeopleId: this.state.selectPeopleId.filter(element => element !== person.userCompetences[0]['asset']['id'])
}, () => console.log(this.state.peopleChecked))
}else{
//If clicked checkbox is not already selected then add that in peopleChecked array
this.setState({
selectPeopleId: [...this.state.selectPeopleId, person.userCompetences[0]['asset']['id']],
peopleChecked: [...this.state.peopleChecked, person]
}, () => {console.log(this.state.selectPeopleId)})
}
}
render() {
return (
<div>
{this.state.people.map(person => (
<div key={person.firstname} className="mb-1">
<input
type={'checkbox'}
id={person.id}
label={person.firstname}
checked={
this.state.peopleChecked.some(
({ asset_id }) => asset_id === person.userCompetences[0]['asset']['id']
)}
onChange={() => this.handleSelect(person)}
/> {person.firstname}
</div>
))}
</div>
);
}
}
You can probably simplify your handleSelect logic a bit. Try breaking it down so you have an array of strings to work with. This is all you need just to toggle the checkboxes:
See sandbox with working code: https://stackblitz.com/edit/react-xsqhwt?file=index.js
handleSelect = person => {
const { peopleChecked } = this.state;
let peopleCheckedClone = JSON.parse(JSON.stringify(peopleChecked));
const personId = person.userCompetences[0].asset.id;
const removeIndex = peopleChecked.findIndex(
person => person.asset_id === personId
);
if (removeIndex >= 0) {
peopleCheckedClone.splice(removeIndex, 1);
} else {
peopleCheckedClone = [...peopleCheckedClone, { asset_id: personId }];
}
this.setState({
peopleChecked: peopleCheckedClone
});
};