I have an array of objects
listOfItems: any[] = [
{text: 'Hola', group: 'espanian'},
{text: 'Hello', group: 'english'},
{text: 'How are you', group: 'english'},
{text: 'Tere', group: 'estonian'},
]
I would like to iterate over an array and create another array with categorized objects
groups: any[] = [
[
{ name: 'english',
items: [
{text: 'Hello', group: 'english'},
{text: 'How are you', group: 'english'},
]
},
{ name: 'espanian',
items: [
{text: 'Hola', group: 'espanian'}
]
},
{ name: 'estonian',
items: [
{text: 'Tere', group: 'estonian'}
]
},
]
What is the most dynamic way to do it? The number of groups could be over 30, so I would like to avoid doing if or equals checks inside the loop
I mean dynamic - is basically without any explicit checks like item.group = 'english'
I have tried to achieve something similar with
let result: any = [];
this.listOfItems.forEach(p => {
var key = p.group;
result[key] = result[key] || [];
result[key].push(p);
})
however the result is not exactly what I am looking for
[
{'english': [
{text: 'Hello', group: 'english'},
{text: 'How are you', group: 'english'},
]
},
{'espanian': [
{text: 'Hola', group: 'espanian'},
]
},
]
CodePudding user response:
- Why
any[]
? - Maps are really great for these types of data manipulations. Check it out:
interface Item {
text: string;
group: string;
}
interface Categorized {
name: string;
items: Item[];
}
const itemList: Item[] = [
{ text: 'Hola', group: 'espanian' },
{ text: 'Hello', group: 'english' },
{ text: 'How are you', group: 'english' },
{ text: 'Tere', group: 'estonian' },
];
const categorize = (list: Item[]): Categorized[] => {
const map: Record<string, Categorized> = {};
list.forEach((item) => {
if (!map[item.group]) {
map[item.group] = { name: item.group, items: [item] };
return;
}
map[item.group].items.push(item);
});
return Object.values(map);
};
console.log(categorize(itemList));
We're only using one loop.
Compiled:
var itemList = [
{ text: 'Hola', group: 'espanian' },
{ text: 'Hello', group: 'english' },
{ text: 'How are you', group: 'english' },
{ text: 'Tere', group: 'estonian' },
];
var categorize = function (list) {
var map = {};
list.forEach(function (item) {
if (!map[item.group]) {
map[item.group] = { name: item.group, items: [item] };
return;
}
map[item.group].items.push(item);
});
return Object.values(map);
};
console.log(categorize(itemList));
CodePudding user response:
Array.reduce
implementation.
const listOfItems = [
{ text: 'Hola', group: 'espanian' },
{ text: 'Hello', group: 'english' },
{ text: 'How are you', group: 'english' },
{ text: 'Tere', group: 'estonian' },
];
const output = listOfItems.reduce((acc, curr) => {
const matchItem = acc.find(item => item.name === curr.group);
if (matchItem) {
matchItem.items.push(curr);
} else {
acc.push({
name: curr.group,
items: [curr]
})
}
return acc;
}, []);
console.log(output);
For those who are concerned about double loop, you can achieve the above one with a single loop using.
const listOfItems = [
{ text: 'Hola', group: 'espanian' },
{ text: 'Hello', group: 'english' },
{ text: 'How are you', group: 'english' },
{ text: 'Tere', group: 'estonian' },
];
const output = listOfItems.reduce((acc, curr) => {
acc[curr.group] ? acc[curr.group].items.push(curr) : acc[curr.group] = {
name: curr.group,
items: [curr]
};
return acc;
}, {});
console.log(Object.values(output));
CodePudding user response:
Try with the below code
const modifiedList = listOfItems.reduce((acc, item) => {
if(!acc[item.group]) acc[item.group]=[item];
else acc[item.group].push(item)
}, {});
let newList = [];
for(let item in modifiedList) {
newList.push({
name: item,
items: modifiedList[item]
})
}
CodePudding user response:
const listOfItems = [
{text: 'Hola', group: 'espanian'},
{text: 'Hello', group: 'english'},
{text: 'How are you', group: 'english'},
{text: 'Tere', group: 'estonian'},
]
const groups = listOfItems.reduce((acc, {text, group}) => {
if(!acc[group]) {
acc[group] = {name: group, items: []}
}
acc[group].items.push({text, group})
return acc;
}, {});
console.log(Object.values(groups));