Deleting item from state array in react - javascript

I have a delete function that looks like this:
this.deleteItem = item => event => {
const { res, selectedItems } = this.state;
res.splice(res.indexOf(item), 1);
selectedItems.splice(res.indexOf(item), 1);
this.setState({
res
});
};
Here, the res is the list of items displayed on the page, and selectedItems is a list of which of those items are selected. When an item from res is deleted, selectedItems should be updated to remove the index of that item from the list.
Whenever I try to delete a specific item however, it just deletes the last item added to the array, rather than the item that corresponds to the index of the target clicked. Something must be going on with the way it's being indexed, but I've been having trouble identifying the source.
I've also tried as referenced here, but this didn't work since I need to take in item as a parameter.
What would be a good way to go about this?
Thank you very much.
EDIT: Changed the function to look like #HMK's response. After an item is deleted, the output of console.log(res) in the render method is an array of objects which has the form:
res
(9) […]
​
0: Object { id: 0, name: "a", description: "description of a", … }
​
1: Object { id: 2, name: "b", description: "description of b", … }
​
2: Object { id: 3, name: "c", description: "description of c", … }
​
3: Object { id: 4, name: "d", description: "description of d", … }
​
4: Object { id: 5, name: "e", description: "description of e", … }
​
5: Object { id: 6, name: "f", description: "description of f", … }
​
6: Object { id: 7, name: "g", description: "description of g", … }
​
7: Object { id: 8, name: "h", description: "description of h", … }
​
8: Object { id: 9, name: "i", description: "description of i", … }
​
length: 9
​
<prototype>: Array []
The output of console.log(JSON.stringify(item,undefined,2)); in the deleteItem function is the object that is deleted from the array.
e.g.:
{
"id": 10,
"name": "j",
"description": "description of j",
"icon": "jIcon",
"selected": false
}
When all items on the page are selected, the output of console.log("selecteditems:", JSON.stringify(selectedItems)):
selecteditems: [0,1,2,3,4,5,6,7,8,9,10]

To take out an item from an array you can do the following:
array.filter(item=>item!==itemToRemove)
So in your example:
this.deleteItem = item => event => {
const filter = getter => val => getter(val) !== item.id
this.setState({
res: this.state.res.filter(filter(({id})=>id)),
selectedItems: this.state.selectedItems.filter(
filter(id=>id)
)
})
}
The problem you have is that res stores an array of objects that have an id (for example: [{id:1}], then selectedItems is an array that stores the id: (for example: [1]).
Array.prototype.filter works in the following way: newArray = array.filter(filterFunction). For each item in array filterFunction is called with that item. If filterFunction returns false then that item is not copied to newArray, it it returns true it is copied to newArray. The original array stays untouched (as it should be with state because you should not mutate it).
So the problem is that your filter function gets an item of the array, decides if it should return true or false (true to keep the item and false to not keep it). So if I filter res the filter function will receive {id:X} (object with an id) but when I filter selectedItems I will receive X (the id).
So the filter function needs to take out element(s) with a certain id, however; with res that id is a property of an object and with selectedItems it is an id. For res you can write a getter function to get the id out of the object: resItem=>resItem.id, give that function an item from res and it'll return an id. For selectedItems the getter function should just return the value it's given because items in selectedItems are ids, so that getter function looks like id=>id
Ok, lets' get back to filter, I have an array of id's called selectedItems [9] and want to remove id with value 9, let's call that idToRemove so I can do: selectedItems.filter(id=>id!==idToRemove). That same function won't work with res because {id:9} never equals 9.
But what if I pass a selector to the filter function, here it gets a bit complicated because functions can return a function:
const idToRemove = 9;
//filter function return true for array item to stay in the copied array
// and false to not include the array item in the copy
const filter = getter => arrayItem =>
getter(arrayItem) !== idToRemove;
//passing filter a getter function will return a function that takes
// an item and uses the getter function on that item to compare it
// to idToRemove
const compareId = filter(id => id);
console.log('keep 9?', compareId(9));
console.log('keep 8?', compareId(8));
//now create a filter function that takes an object and uses
// the getter to get object.id and compares that to idToRemove
const compareObjectWithId = filter(object => object.id);
console.log('keep {id:9}?', compareObjectWithId({ id: 9 }));
console.log('keep {id:8}?', compareObjectWithId({ id: 8 }));
So compareId is a function we can use to filter out an item from selectedItems and compareObjectWithId is a function we can use to filter out an item from res here is how the filter is used:
const idToRemove = 9;
const createFilterFunction = getter => arrayItem =>
getter(arrayItem) !== idToRemove;
console.log(
'[2,9] remove id 9',
[2, 9].filter(createFilterFunction(id => id))
);
console.log(
'[{id:2},{id:9}] remove id 9',
[{ id: 2 }, { id: 9 }].filter(
createFilterFunction(object => object.id)
)
);
For completion I will add modern code to remove a key from an object (don't try this on stack overflow code snippet because it's using an ancient babel version)
const org = {a:1,b:2};
const withoutA = Object.fromEntries(
Object.entries(org).filter(([key])=>key!=='a')//removed key 'a'
)

Related

Check if object of any array exist in an another array, if not exists then push onto another array , or else remove it from another array

I'm working on a checkbox ui react web app, where in, on check we dispatch items array with a object in it, and on uncheck also we dispatch items array with object in it. So I need to add this logic - Check if object of any array exist in an another array, if not exists then push onto another array , or else remove it from another array
let items1 = [{ name: "a" }, { name: "b" }, { name: "c" }];
let items2 = [{ name: "a" }, { name: "d" }, { name: "e" }];
const commonItems = items1.filter((x) => items2.some((y) => y.name === x.name));
if (!commonItems) {
items1.push(...items2);
} else {
items1 = items1.filter((x) => items2.some((y) => y.name !== x.name));
}
console.log(items1);
console.log(items2);
Check if object of any array exist in an another array, if not exists then push onto another array , or else remove it from another array,
Is this code corect for above logic?
Couple of issues. 1) commonItems is always a truth value because filter returns empty array when no results, so always going to else block 2) Else block filter is not correct. (updated here to use !some)
let items1 = [{ name: "a" }, { name: "b" }, { name: "c" }];
let items2 = [{ name: "a" }, { name: "d" }, { name: "e" }];
const commonItems = items1.filter((x) => items2.some((y) => y.name === x.name));
console.log(commonItems)
if (commonItems.length < 1) {
items1.push(...items2);
} else {
items1 = items1.filter((x) => !items2.some((y) => y.name === x.name));
}
console.log(items1);
console.log(items2);

How can i find out the last entered object in an array of objects, where similar objects can be entered multiple times

Suppose,
i have an object with properties, details : {"name","id"} , now there is an array that holds collection of details. now suppose , an object with {name:max, id:55} is pushed in the array more than once. how do i find out the last entered {name:max,id:55} from that array using TypeScript .
You can do it with pure JavaScript and lastIndexOf:
const myArray = [{
name: "max",
id: 55
}, {
name: "john",
id: 13
}, {
name: "susan",
id: "123"
}, {
name: "max",
id: 55
}];
const lastEntered = (name, id) => {
var matches = myArray.filter(e => e.name == name && e.id == id);
return matches.length - 1;
}
console.log(lastEntered("max", 55));
Array is last in first out data structure.
So the last in element will occupy the element with the largest index.
So you may get the element by array[array.length-1]
You can use reduce to change the array to an object, where the key is a value from the object, and the value is the last index of that key.
const details = [{ name: 'max', id: 55 }];
const detailsMap = details.reduce((acc, person, index) => {
acc[person.id] = index;
return acc;
}, {});
const lastIndexOfMax = detailsMap[55];
Here we set the id from the detail object to the key (because I assumed that each id is unique). When we enter that key into the details map, it returns to use the index of the array where that id is last located.

Is there any lodash method to update an object inside an array, where the item matched is going to have a new key?

Im adding a checkbox options and only have to update my object with a new key
so if I uncheck a item in a list i want to update the object
[ { id: 1 }, { id: 2 } ]
after unchecked:
[ { id: 1 }, { id: 2, unChecked: false } ]
any method to toggle this states?
thanks in advance
You can do it using native javascript and array map method. In the function check if the id matches then add the key there
let obj = [{
id: 1
}, {
id: 2
}]
function updateObj(obj, objId, key) {
return obj.map((item) => {
if (item.id === objId) {
return {
id: item.id,
[key]: false
}
} else {
return {
id: item.id
}
}
})
}
console.log(updateObj(obj, 2, 'unchecked'))
You can do this without lodash and using the .map method by adding the unChecked property if the id is not in the checked array by using .includes().
See working example below:
const checked = [2, 3], /* array holding all checked values */
arr = [{id: 1}, {id: 2}],
res = arr.map(({id}) => checked.includes(id) ? {id, unChecked: false} : {id});
console.log(res);

Javascript - How to filter object values with search input and get its key based on that?

const obj ={
1: {name:"josh",age:2, symbol: "abc", id: 1},
2: {name:"mike",age:4, symbol: "efg", id: 2}
}
const search = (input) => {
return Object.values(obj).filter(item => {
return item.name.includes(input)
})
}
search("mi")
// returns: [ { name: 'mike', age: 4, symbol: 'efg', id: 2 } ]
How can I access the found objects id (id:1)
How can I get the found objects key (1: ...)
I am trying to find 1 and 2 of the object inside my array which matched my search input (see function)! In my filter I search for an object inside my array and see if one of those objects matches (or not) my search input. Then based on that I want to find the id of THAT object and its key
Thanks!
You had it right, except when you used Object.values you lost the object keys, check demo below
const obj ={
1: {name:"josh",age:2, symbol: "abc", id: 1},
2: {name:"mike",age:4, symbol: "efg", id: 2}
}
const search = (input) => {
return Object.keys(obj).filter(key => {
return obj[key].name.includes(input)
})
.map(foundKey => ({...obj[foundKey], key: foundKey }))
}
const result = search("mi")
console.log(result)
Change the search for this:
const search = input => Object.values(obj).findIndex(item => item.name.includes(input));
Your Search:
const index = search('mike');
Your ID:
const id = Object.keys(obj)[index];
Your Item:
const item = obj[id];

ES6 filter - how to return an object instead of an array?

I have bunch of array of object, I want to get particular object using filter, but I got array using below code.
const target = [{
name: 'abc',
id: 1
}, {
name: 'def',
id: 2
}]
const x = target.filter(o => o.id === 1)
console.log(x)
As said in the comments, filter won't allow you to get a particular object from an array - it just returns another array which elements satisfy the given predicate. What you actually need is Array.prototype.find(). Quoting the doc:
The find() method returns the value of the first element in the array
that satisfies the provided testing function. Otherwise undefined is
returned.
So your code looks like this:
const target = [{
name: 'abc',
id: 1
}, {
name: 'def',
id: 2
}];
const x = target.find(o => o.id === 1);
console.log(x); // {name: "abc", id: 1}
array.filter always return array. But you can try this-
const target = [{
name: 'abc',
id: 1
}, {
name: 'def',
id: 2
}]
let obj = {}
const x = target.filter( (o, index) => {
if(o.id === 1)
obj = target[index]
})
console.log(obj)
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
The find() method returns the value of the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, undefined is returned.
Array.prototype.filter will return array containing elements from original array that passed test function.
If you are sure that id's are unique simply do x[0] to get result.
It's very easy just get first item in retrned as:
const target = [{name: 'abc', id: 1}, {name: 'def', id: 2}]
const x = target.filter(o => o.id === 1)
console.log(x[0])

Categories

Resources