I have a grid that shows the list of time (from time to time)
for example
timeList=[{from:12:00:00 , to:14:00:00 ,id:10}{from:08:00:00 , to:09:00:00 ,id:11{from:05:00:00 , to:10:00:00 ,id:12}}]
time=[{Value:12:00:00 id:10}
{Value:14:00:00 id:100}
{Value:08:00:00 id:11}
{Value:09:00:00 id:110}
{Value:05:00:00 id:12}
{Value:10:00:00 id:15}
]
To delete an item I have this code
deleteTimeView(data) {
////date ==>{from:12:00:00 , to:14:00:00 ,id:10}
let indexOfDeleted = -1;
let time = this.state.time;
let timeList=this.state.timeList;
this.state.time.forEach((item, index) => {
if (item.Id === data.id) {
indexOfDeleted = index;
}
})
time.splice(indexOfDeleted, 1);
time.splice(indexOfDeleted, 1);
timeList.splice(indexOfDeleted, 1);
this.setState({
time: time,
timeList: timeList
});
}
My problem is that when I want to delete id12, I have index 4, but I do not have this index in timeList.
I have no problem deleting that item in time, but how can I delete it from timeList as well
Firstly, don't use .splice, it mutates the array and in this case means you're directly mutating state. Never mutate the state in React.
Secondly, a simple filter approach based on the ID should satisfy your needs:
deleteTimeView(data) {
this.setState({
time: this.state.time.filter(t => t.id !== data.id),
timeList: this.state.timeList.filter(t => t.id !== data.id)
});
}
Related
I have an array of objects... each object has a boolean key called 'included' changed when clicking on a button in html
I need the array to be sorted the objects with true value on the top and then the objects with false value including that the last changed object with false value put at the last of array and if changed to true put after the objects with truthy value
the below is what I did ... is that optimum or there ara another way?
<mat-icon *ngIf="product.included"
(click)="applyAction('exclude', product.id)">
remove
</mat-icon>
<mat-icon *ngIf="!product.included"
(click)="applyAction('include', product.id)">
add
</mat-icon>
applyAction(action: action, id: number): void {
const selectedProduct = this.productTypes.find((i) => i.id === id);
if (action === 'exclude') {
selectedProduct.included = false;
} else {
selectedProduct.included = true;
}
this.productTypes = this.productTypes.filter((i) => i.id !== id);
this.productTypes.push(selectedProduct);
this.productTypes.sort((a, b) => Number(b.included) - Number(a.included));
}
Not sure about optimum but you could use .findIndex and use .splice remove the element and then do a .push to push the latest edited element to the end:
let productTypes = [{id: 1, included: true}, {id: 2, included: false}, {id: 3, included: true}];
const applyAction = (action, id) => {
const selectedProductIdx = productTypes.findIndex((i) => i.id === id);
const selectedProduct = productTypes[selectedProductIdx];
if (action === 'exclude') {
selectedProduct.included = false;
} else {
selectedProduct.included = true;
}
productTypes.splice(selectedProductIdx, 1);
productTypes.push(selectedProduct);
}
applyAction('exclude', 1);
applyAction('include', 2);
console.log(productTypes);
You can avoid the filter and push calls by retrieving the index of the product and avoid updating if either the product is not present or the value doesn't need a change.
Worst case scenario this function will traverse the productTypes array once for findIndex and sorting is n * logn:
applyAction(action: action, id: number): void {
const productIndex = this.productTypes.findIndex((i) => i.id === id);
if (productIndex === -1) return; // Product not found
const included = this.productTypes.included;
if ((included && action !=== 'exluded') || (!included && action === 'excluded')) {
// No update needed
return;
}
this.productTypes[productIndex].included = !included;
this.productTypes.sort((a, b) => Number(b.included) - Number(a.included));
}
I would say use one temporary array and push all the element except the particular last updated id element and end of the function push the last updated id element. It will took O(n) time and space.
applyAction(action: action, id: number): void {
let findInd = -1, temp_arr=[];
this.productTypes.forEach((element, index)=>{
if(element.id !=id) temp_arr.shift(element);
else findInd = index;
});
temp_arr.push(this.productTypes[findInd]);
this.productTypes = temp_arr;
}
I have created a dynamic form which can have rows added and removed and are stored in a state array.
I need to remove the index passed into the function from the array, without storing a null or empty value.
This is my current code for removing the rows however this simply removes the last row and not the one required at index
const removeRow = (index) => {
setLocationRows((current) =>
current.filter((employee, i) => {
return index !== i;
})
);
};
This code removes the required index however sets the value to null / empty which messes up when after removing and adding rows.
setLocationsObj((current) => {
const copy = { ...current };
delete copy[index];
return copy;
});
Joe.
Im supposing you have something like this:
const [locationRows, setLocationRows] = useState([]);
const removeRow = (index) => {
setLocationRows(locationRows.filter((e,i)=> i !== index))
};
If so, try the above code.
For the complete CRUD operation you can use the following:
const addRow = (newRow) => {
setLocationRows([... locationRows, newRow])
};
const updateRow = (rowData) => {
setLocationRows(locationRows.map(e => {
if(e.id === rowData.id) return rowData;
else return e;
});
};
I hope this can help you!
I recently had to do something very similar and used the array splice method, as it allows you to remove the element at a specific index.
const removeRow = (index) => {
setLocationRows((rows) =>
// create deep copy
const newRows = JSON.parse(JSON.stringfy(rows));
// remove 1 element at index
newRows.splice(index, 1);
return newRows;
);
};
If you are dealing with any sort of nested array it's important to create a deep copy of that array, as the const copy = [...rows] method only creates a shallow copy and can cause all sorts of bugs when trying to manipulate the data further.
Hope this helps!
I have two functions , one of them adds an item in array and the other one delete from that array using React JS (hooks).[Both are handler of click event].
What I have works incorrectly.
``id`` comes from ``contact.length`` and I deleted it with``contacts.splice(id, 1)``.
I dont have any idea why it has this problem.
it doesnt delete what would be clicked but a random one.
function handleAddRecord(nameValue, phoneValue) {
setContacts([...contacts , {
id : contacts.length,
name : nameValue,
phone : phoneValue
}])
}
function handleDelete(id) {
console.log("manager", id);
const newContacts = contacts.splice([id],1);
setContacts([...newContacts]);
}
One of the issue on the implementation is id generation keeping it array length could lead to issue as you delete and add elements there could be scenarios where there is same id for multiple items.
One of most widely used generator is uuid https://www.npmjs.com/package/uuid
Usage
const uuid = require("uuid");
uuid.v4(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
Now use this in your implementation
Add Operation:
const handleAddRecord = (nameValue, phoneValue) => {
const newRecord = {
id: uuid.v4(), // This should be unique at all times
name: nameValue,
phone: phoneValue,
};
setContacts([...contacts, newRecord]);
};
Delete Operation:
Use filter rather than splice as for splice you'll need to find the index of the element with id. But with Filter it can be done is a single line
const handleDelete = (id) => {
setContacts(contacts.filter(item => item.id !== id));
};
Here we're assuming that id is the index of the element to be removed.
The splice function returns the removed elements, thus is not useful to take its result. Instead, make a copy of the array first, then remove the undesired element:
function handleDelete(id) {
console.log("manager", id);
const newContacts = [...contacts];
newContacts.splice(id,1);
setContacts(newContacts);
}
That's because splice alters the array itself.
More here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
Ok, id return index of current map?
Follow this example:
const assoc = [...contacts];
assoc.splice(id, 1);
setContacts(assoc);
You can delete the item by finding its index from array.
For Example:
function handleDelete(id) {
console.log("manager", id);
const index = contacts.findIndex((x) => x.id === id);
const newContacts = [
...contacts.splice(0, index),
...contacts.splice(index + 1),
];
setContacts(newContacts);
}
You need undestand, every time when i'll remove a item from a array of a index, that this index has use unique key... When React remove a item 6 (a example) this is remove of array first, and when react re-render function react can delete another component, because a array exist key 6, wehn you have more 6 item from array... Understand?
Follow a example:
import React, { useState } from 'react';
function User(data) { // data is a array
const [contacts, setContacts] = useState(data); // data return a list of contacts
/* contacts result a array object and has the following attributes
[{
name: 'Cael',
tel_number: '+55 11 9999-999',
e_mail: 'user#example.com',
! moment: "2021-06-15T05:09:42.475Z" // see this a date in ISO string
}]
*/
// about moment atribute:
// this atribute result when use `new Date().toISOString()`
// and this value is added in the moment that create a object in array list
// It's mean that every time result a unique key
const deleteFn = (val) => { // val result index of component and item array
const assoc = [...contacts];
assoc.splice(val, 1);
setContacts(assoc);
}
return (
<div>
{!!contacts.length &&
contacts.map((assoc, i) => { // variable i result in your id
const { moment, name, e_mail, tel_number } = assoc; // moment use a unique key
return (
<li key={moment}>
<span>{name}</span>
<span>{e_mail}</span>
<span>{tel_number}</span>
<button type="button" onClick={() => deleteFn(i)}>Delete</button>
</li>
);
})}
</div>
);
}
export default User;
I hope, this helpfull you!
remove an object from an array is not working properly ,but it add object perfectly
const addItem =(selected)=>{
let data = selectedItems ? [...selectedItems] : [];
if (data.length) {
let index = data.indexOf(selected);
console.log(index);
if (index !== -1) {
data.splice(index, 1);
setSelectedItems(data);
} else {
data.push(selected);
}
} else {
data.push(selected);
}
console.log("selected", selectedItems);
setSelectedItems(data);
}
render button function add or remove on click it
<div className="file-list">
<MappedElement
data={[{ _id: 1 }, { _id: 2 }]}
renderElement={(value, index, arr) => {
let check=selectedItems.some((obj) => obj._id === value._id);
console.log("check", check);
return (
<DocumentCard key={index} className={file-list-item ${check ?
"active" : ""}}
onClick={() => addItem(value, arr, index)} /> ); }} />
</div>
For a selectedItems array that looks like:
const selectedItems = [
{ _id: 1, /* other fields */ },
{ _id: 2, /* other fields */ },
{ _id: 3, /* other fields */ },
/* other objects */
];
And a selected object that looks like:
const selected = { _id: 1 };
In order to perform the desired behavior, which is: if the element exists, remove it, else, add it, you can write the following:
// copy selected items with a fail-safe empty array
const data = selectedItems ? [...selectedItems] : [];
// find index of selected element
const removalIndex = data.findIndex(({ _id }) => (_id === selected._id));
// if selected element exists in data array, remove it
if (removalIndex !== -1) {
data.splice(removalIndex, 1);
}
// if selected element doesn't exist in data array, add it
else {
data.push(selected);
}
// update selected elements
setSelectedItems(data);
NOTE: if your array of selected items contains duplicates, meaning multiple objects that contain the same value for _id, then this approach will be removing only the first instance of those. If you want to remove all of them, you'll have to use a loop or recursivity.
Your problem is probably in indexOf method you're using.
You can not use this to find objects in your array.
There are several options you can use. You can use find or findIndex and pass a callback to find an object by the specified (preferably unique property of the object).
Example
let found = data.findIndex(item => item.id === selected.id);
Use
const addItem =(selected)=>{
let data = selectedItems ? [...selectedItems] : [];
if (data.length) {
let index = data.findIndex(value => value._id === selected._id)ж
console.log(index);
if (index !== -1) {
data.splice(index, 1);
} else {
data.push(selected);
}
} else {
data.push(selected);
}
console.log("selected", selectedItems);
setSelectedItems(data);
}
I am trying to create a cart with React js and Redux and I have one problem.
Tried many ways but I keep failing that when I add multiple items (food/drink) to the list then everything seems to be working, but then when I want for example add additional drink to the existing choice my list gets overwritten. Here is the code I have it now:
const addItemToCart = item => {
if (cartItems.length) {
cartItems.forEach(itemToCheck => {
if (item.name === itemToCheck.name) {
addCountToItem({ ...itemToCheck, count: itemToCheck.count + 1 });
} else if (item.name !== itemToCheck.name) {
addToCart({ ...item, count: 1 });
}
});
} else if (cartItems.length === 0) {
addToCart({ ...item, count: 1 });
}
};
Idea is that I can have multiple items on the list and unlimited number of same items within the list. So basically, I should be able to have 5 pizzas of the same type, 3 beers of different type etc.
I guess like any other cart. Thanks in advance.
update:
Here the code for addCountToItem. I deleted it but it was going something in this direction
state.cartItems[findIndex(...)] = data.cartItem
a basic way to solve your problem is
`let index=cartItem.findIndex(temp=>temp.name===item.name);
if(index>-1){
cartItem[index].count++;
}
else{
cartItem.append({...item, count: 1 })
}`
try not to mutate cartItem object
We need too see to all the related code to successfully answer.
Here I give sample example, updatedCartItems keeps the updated cart, you can do whatever you want. In general, this type of manipulation must be in the cart reducer, but you didn't post the the reducer code.
const addItemToCart = item => {
let updatedCartItems = [...cartItems];
updatedItemIndex = updatedCartItems.findIndex(
item => item.name === itemToCheck.name // better to check with some kind of id if exists
);
if (updatedItemIndex < 0) {
updatedCartItems.push({ ...item, count: 1 });
} else {
const updatedItem = {
...updatedCartItems[updatedItemIndex]
};
updatedItem.count++;
updatedCartItems[updatedItemIndex] = updatedItem;
}
//updatedCartItems => the new cart
};
for shopping card, we need to have cartItems property as array in our state, and every time we click on the addToCart button, we will push that item to that array and then we render that array in the cartDropdown component or the checkout page.
since you are able to add single item to the cart, it means that you have correct set up for redux. in order to add same item to the cart more than once, we just need to write a simple utility function.
here is the utility function:
export const addItemToCart = (cartItems, cartItemToAdd) => {
//find(condition) finds the first item in the array based on the condition.
const existingCartItem = cartItems.find(item => item.id === cartItemToAdd.id);
if (existingCartItem) {
//in order for change detection to trigger we have to rerender
//otherwise our quantity property will not be updated
//map will return a new array
//we need to return new versions of our state so that our component know to re render
//here we update the quantity property
return cartItems.map(item =>
item.id === cartItemToAdd.id
? { ...cartItemToAdd, quantity: item.quantity + 1 }
: item
);
}
//when you first time add a new item, sine exixtingCartItem will be falsy, it will pass the first if block and will come here
//quantity property gets attached the first time around since this if block wont run when it is a new item.
//in the beginning cartItems array is empty. every time you add a new item to this array, it will add "quantity:1" to this item object.
return [...cartItems, { ...cartItemToAdd, quantity: 1 }];
};
here is the action to add item to the cart
export const CartActionTypes = {
ADD_ITEM: "ADD_ITEM",
};
export const addItem = item => ({
type: CartActionTypes.ADD_ITEM,
payload: item
});
since you are able to add single item to the cart, it means that you have correct set up for redux. you need to dispatch this to the reducer in the component that you render addToCart button. here is the cart reducer where the case is CartActionTypes.ADD_ITEM.
import { addItemToCart } from "./cart.utils";
case CartActionTypes.ADD_ITEM:
return {
...state,
cartItems: addItemToCart(state.cartItems, action.payload)
};