0

I have this object in state below and I would like to filter and return a new state with the selected object removed based on the id that is passed to an onClick.

Object in state:

const [data, setData] = useState({
 category1: [
   { category: "category1", id: 1, subcategory: "bob"  },
   { category: "category1", id: 2, subcategory: "mike" },
 ],
 category2: [
   { category: "category2", id: 3, name: "swain" },
   { category: "category2", id: 4, name: "teemo" },
   { category: "category2", id: 5, name: "garen" }
 ]
});

Previously, I was able to remove a whole category itself with onClick with a passed in categoryName as the parameter with this code:

const filteredCategoryData = Object.fromEntries(
   Object.entries(data).filter(([key, value]) => key !== category)
);

setData(filteredCategoryData);

Now I want to extend on this logic and loop through the objects of each category and remove the subcategory based on id. I tried:

const filteredsubCategoryData = Object.fromEntries(
       Object.entries(data).filter(([key, value]) => {
       return value.filter(subCategory => subCategory.id !== id) 
    });
);

setData(filteredCategoryData);
/* Output -> same original object: 
          {
             category1: [{...}, {...}], category2: [{...}, {...}, {...}]
          }
   If idToRemove matches first object (index 0) of category1, new object 
   should return :
   category1: [{...}], category2: [{...}, {...}, {...}]
*/

But this filteredsubCategoryData is returning the original intact state and I am confused on why. Thank you for your help!

4
  • just to be sure: I don't see that you ran the function setData in the now I tried portion? Commented Jan 31, 2022 at 21:22
  • can you make it clear a bit? what is the id of subCategory and what is the id in this expression !== id? Commented Jan 31, 2022 at 21:22
  • @Zargold Sorry I just added it in snippet but it is in my code. Commented Jan 31, 2022 at 21:36
  • @vuongvu Sure, id of subCategory is the id of each subcategory object within each category. (ex. 0: { category: "category1", id: 1, subcategory: "bob" }) !== id is the filter where I want to return a new state with the given id removed in my function: const removeSubcategory = (id) = {...} Commented Jan 31, 2022 at 21:37

2 Answers 2

1

The filter() method either removes an entire item or leaves it untouched, depending on whether the passed callback returns a true or false value. So the inner filter in your code is accomplishing nothing - its result is an array object, which is a truthy value, but the array itself is discarded. Instead of the outer filter(), you should be using map():

const filteredsubCategoryData = Object.fromEntries(
    Object.entries(data).map(([key, value]) => {
        return [key, value.filter(subCategory => subCategory.id !== id)]
    });
);
Sign up to request clarification or add additional context in comments.

1 Comment

This is exactly what I was looking for, thank you so much!
0

It seems like this has a few problems:

{
 category1: [
   0: { category: "category1", id: 1, subcategory: "bob"  },
   1: { category: "category1", id: 2, subcategory: "mike" },
 ],
 category2: [
   0: { category: "category2", id: 3, name: "swain" },
   1: { category: "category2", id: 4, name: "teemo" },
   2: { category: "category2", id: 5, name: "garen" }
 ]
}

This is not the syntax for an array:

category2: [
       0: { category: "category2", id: 3, name: "swain" },
       1: { category: "category2", id: 4, name: "teemo" },
       2: { category: "category2", id: 5, name: "garen" }
     ]

It looks like an object almost?

category2: {
       0: { category: "category2", id: 3, name: "swain" },
    }

Is the correct way to notate an object. Object have curly brackets. You can choose to use an array if the keys are just numbers, but you may have your reasons.

In either case assuming that you did intend for an array:

.filter(([key, val] => val.filter(some => some.id === id)

I'm not sure I understand your usecase enough to be sure if you want !== id or === but in either case. In any case, Array.prototype.filter will only return an array but it requires the output of the filter function to be a Boolean. As you have it currently, you have a filter function nested within a filter function which means the output of the nested filter will always be an array which is always truthy so the outer filter will always keep the same as it was.

In JavaScript (as opposed to in python): an empty array is still truthy: so even if you filtered out all the elements in your filter it would result in: [] which is truthy and thus it would keep the parent element. I assume you may have wanted to check whether the output of the nested filter is empty? If so you can do:

const filteredsubCategoryData = Object.fromEntries(
       Object.entries(data).filter(([key, value]) => {
       return value.filter(subCategory => subCategory.id !== id).length // .length of 0 would mean there is nothing left
    // after the filter
    // in other words filter keep only those where the result of .filter is no elements
    // or you might do the reverse by saying: .length === 0
    // I want to keep only the categories (like `category1`) if they
    // have no elements with that `id` meaning after my .filter function runs
    // no elements remain and the length is 0.
    });
);

Instead you probably intended for valueArray.some(value => value.id !== id) Aka if any of the values in the valueArray are not equal to the id be true. You decide if you want to negate that some to say any vs none using the !. .some achieves the same as running .filter(callback).length: Are there any elements remaining after we run this filter? If so then return true. If not return false. Depending on your usecase you may want:

  • !categorySubcategories.some(callback): This will end up true if nothing returns true in the callback. Which means keep the parent entry category1.
  • categorySubcategories.some(callback): This will end up false if nothing returns true which means discard it from the top level entry.

5 Comments

Sorry I don't know why I added the index. It is correct now. Can you expand on the .some with the code that I tried? .some() seems to return a boolean. I am trying to return an updated array without that object matching that id. thank you!
So in other words the old object contained subcategories that have the "unwanted" id. Any categories that contain the unwanted subcategory (which we know are unwanted because the subcategory.id === idToRemove? If that's the case then you want to Filter out all the categories where !CategorySubcategories.some(subcat => subcat.id === idToRemove). In other words, if I could not find any with the subcategory ID that I want to remove then it can remain the same? I also recommend better naming convention be specific about the name of the variable id is it maybe unwantedId or onlyWantedId.
Hi @JohnMcGellan: maybe it would be helpful for you to show the desired output in the question just to be sure we know what your desired behavior is: Like: OriginalState: blah Some Function Output: bla. So to get that you'll have to manually figure out what your output is.
thank you for the feedback! I will work on the naming. Your thoughts are exactly what I want to do. If idToRemove does not match any of the subcategories then we want to keep them the same. If idToRemove matches, we want to remove that object from the state object. How would you modify my code to do that? Sorry I am bad with JS.
Thank you for all your feedback on best practices and help! You had all the right things except it was my fault I wasn't clear enough. The answer below is what I was looking for and what you were alluding to.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.