I have two states one has categories and the other has items.
all i need is one final state that combines both states, either by merging one into the other or by making a third state.
the categories state looks like this:
{
category1: { items: [] },
category2: { items: [] }
}
and the second state is an array of items and it looks like this:
[
{"id": "1", itemInfo: { name: "itemName", category: "category1" }},
{"id": "2", itemInfo: { name: "itemName", category: "category1" }},
{"id": "3", itemInfo: { name: "itemName", category: "category2" }},
{"id": "4", itemInfo: { name: "itemName", category: "category1" }},
{"id": "5", itemInfo: { name: "itemName", category: "category2" }},
{"id": "6", itemInfo: { name: "itemName", category: "category2" }}
]
i need it to be like this:
{
category1: { items: [
{"id": "1", itemInfo: { name: "itemName", category: "category1" }},
{"id": "2", itemInfo: { name: "itemName", category: "category1" }},
{"id": "4", itemInfo: { name: "itemName", category: "category1" }}
]},
category2: { items: [
{"id": "3", itemInfo: { name: "itemName", category: "category2" }},
{"id": "5", itemInfo: { name: "itemName", category: "category2" }},
{"id": "6", itemInfo: { name: "itemName", category: "category2" }}
]}
}
help. please.
this is what i tried and didn't work, it either duplicates the categories and i end up with 3 "category1" and 3 "category2" or the new items overwrite previous items.
const newFinalList = items.map((item: any) => {
return {[item.itemInfo.category]: {items: [item]}}
})
setFinalState((prevObj: any) => ({ ...prevObj, ...newFinalList }))
The issue is that the array mapping returns a new array, but you are wanting to map the array to the categories
state shape which is an object. The mapping logic returns an object for each item element in the source array.
const newFinalList = items.map((item: any) => {
return { // <-- new object for each item
[item.itemInfo.category]: { items: [item] } // <-- only one item in each items array
}
})
Reduce the array into an object.
Example:
// Iterate the items array and reduce into an object
const newFinalList = items.reduce((categories, item) => {
// If the item category object doesn't exist, create it
// with an initially empty items array
if (!categories[item.itemInfo.category]) {
categories[item.itemInfo.category] = {
items: [],
};
}
// Push the current item into the items array
categories[item.itemInfo.category].items.push(item);
// return the categories object
return categories;
}, {});
const items = [
{ id: "1", itemInfo: { name: "itemName", category: "category1" } },
{ id: "2", itemInfo: { name: "itemName", category: "category1" } },
{ id: "3", itemInfo: { name: "itemName", category: "category2" } },
{ id: "4", itemInfo: { name: "itemName", category: "category1" } },
{ id: "5", itemInfo: { name: "itemName", category: "category2" } },
{ id: "6", itemInfo: { name: "itemName", category: "category2" } },
];
const newFinalList = items.reduce((categories, item) => {
if (!categories[item.itemInfo.category]) {
categories[item.itemInfo.category] = {
items: [],
};
}
categories[item.itemInfo.category].items.push(item);
return categories;
}, {});
console.log({ newFinalList });
If you are using this to merge some passed props/context values, it is a React anti-pattern to also store this derived "state" into React state. Either compute and use the value directly, or use the React.useMemo
hook to compute and provide a stable reference value.
const newFinalList = React.useMemo(() => items.reduce((categories, item) => {
if (!categories[item.itemInfo.category]) {
categories[item.itemInfo.category] = {
items: [],
};
}
categories[item.itemInfo.category].items.push(item);
return categories;
}, {}), [items]);