Reputation: 91
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!
Upvotes: 0
Views: 68
Reputation: 2092
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.Upvotes: 0
Reputation: 541
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)]
});
);
Upvotes: 1