1

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]
helloWorld
  • 105
  • 7
  • 1
    Don't mutate use [Array.prototype.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) to create a new array with the item removed. If you do this and the component still renders that only means you didn't optimize anything (pure components or useMemo) – HMR Mar 11 '20 at 17:22

1 Answers1

3

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'
)
HMR
  • 30,349
  • 16
  • 67
  • 136
  • I see what you're saying, though this didn't work -- `console.log(JSON.stringify(item,undefined,2)` prints out the correct item that should be deleted (and it is deleted from the UI view), but the length of `res` or `selectedItems` does not change after this function is called. Should the `.filter` be removing the item from the array? This doesn't appear to be the case and I'm still having the same problem – helloWorld Mar 11 '20 at 18:00
  • thank you very much for clarifying. I have updated my question with the results of the console.log outputs -- though the item is being removed from `res`, it's still not being removed from `selectedItems`. What would you suggest? – helloWorld Mar 11 '20 at 18:19
  • thank you for that suggestion -- I've updated my code but the item deleted from `res` is still not being deleted from `selectedItems`. Why do you think this is the case and what can I do to figure out the problem? – helloWorld Mar 11 '20 at 18:32
  • `selectedItems` is a list of all the selected (checked) items on the page. I edited my answer to include the the `console.log` of when all the items on the page are selected. When an item is deleted through the `deleteItem` function, it deletes the item from the `res` array, but the selectedItems array remains unchanged and I am struggling to understand why since `.filter` is being applied to both. the item needs to be deleted from both arrays – helloWorld Mar 11 '20 at 18:44
  • when I remove the first item of state.res and then select the (new) first item, selected items is just `[1]`. I think it's going by the ids and not the indices, since I am starting the ids with 0 as well – helloWorld Mar 11 '20 at 19:20
  • wow!!!! you are my hero!!!!!!! thank you so much!!!!!!! If you have a moment, would you mind explaining your syntax and why the getter was the key to solving this issue? I'm not sure I completely follow and would like to understand your expert logic :) – helloWorld Mar 11 '20 at 19:50
  • @helloWorld Updated my answer, passing functions to functions that return functions that you can pass to a function (in this case to Array.prototype.filter) looks complicated but it's more something you can get used to and makes solving complicated problems much easier. I will delete my comments here because they probably aren't of any use to others in the future. – HMR Mar 11 '20 at 20:24
  • 1
    @HMF Thanks again for all your help and support! – helloWorld Mar 13 '20 at 15:02