Javascript - agrupar matriz de objetos por valores comuns com rótulo
Estou tentando transformar uma matriz de objetos em outra matriz de objetos agrupando por um valor específico e adicionando esse valor como rótulo e retirando-o do objeto na nova matriz.
Entrada: Por exemplo, eu tenho esta matriz de objetos:
let tech = [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
];
Esperado: estou tentando descobrir como posso chegar a isso, onde há um rótulo para cada um dos agrupamentos exclusivos e array de opções contendo os valores desse agrupamento.
[
{
label: "Front End",
options: [
{ id: 1, value: "HTML" },
{ id: 2, value: "React" },
],
},
{
label: "Back End",
options: [
{ id: 3, value: "Node" },
{ id: 4, value: "PHP" },
],
},
]
O mais próximo que consegui chegar foi usando reduzir para agrupar pela chave de agrupamento:
const groupedTech = tech.reduce((acc, value) => {
// Group initialization
if (!acc[value.grouping]) {
acc[value.grouping] = [];
}
// Grouping
acc[value.grouping].push(value);
return acc;
}, {});
O que me dá isso:
{
"Front End": [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
],
"Back End": [
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
],
}
Mas isso retorna o objeto não um array e não remove o valor de agrupamento. Não fui capaz de descobrir como agrupar corretamente porque na matriz de objetos não encontrei uma maneira eficiente de comparar para ver se o agrupamento existe e, se houver, adicionar a essa matriz aninhada. Eu ficaria melhor usando algo como .map () ? Aprecie qualquer oportunidade / aprendizado!
Respostas
Você está muito perto, basta envolver as entradas de valor-chave do resultado obtido em uma mapfunção:
let tech = [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
];
const groupedTech = Object.entries(
// What you have done
tech.reduce((acc, { id, grouping, value }) => {
// Group initialization
if (!acc[grouping]) {
acc[grouping] = [];
}
// Grouping
// FIX: only pushing the object that contains id and value
acc[grouping].push({ id, value });
return acc;
}, {})
).map(([label, options]) => ({ label, options }));
console.log(groupedTech);
Você só precisa fazer mais uma manipulação com Object.entriese.map
let tech = [
{ id: 1, grouping: 'Front End', value: 'HTML' },
{ id: 2, grouping: 'Front End', value: 'React' },
{ id: 3, grouping: 'Back End', value: 'Node' },
{ id: 4, grouping: 'Back End', value: 'PHP' }
]
const groupedTech = tech.reduce((acc, value) => {
// Group initialization
if (!acc[value.grouping]) {
acc[value.grouping] = []
}
// Grouping
acc[value.grouping].push(value)
return acc
}, {})
const res = Object.entries(groupedTech).map(([label, options]) => ({
label,
options
}))
console.log(res)
Uma pequena variação nas outras duas respostas se você quiser obter exatamente a saída que você especificou:
let tech = [{
id: 1,
grouping: "Front End",
value: "HTML"
},
{
id: 2,
grouping: "Front End",
value: "React"
},
{
id: 3,
grouping: "Back End",
value: "Node"
},
{
id: 4,
grouping: "Back End",
value: "PHP"
},
];
const groupedTech = Object.entries(
tech.reduce((acc, value) => {
// Group initialization
if (!acc[value.grouping]) {
acc[value.grouping] = [];
}
// Grouping
acc[value.grouping].push({
id: acc[value.grouping].length+1,
value: value.value
});
return acc;
}, {}))
.map(([label, options]) => ({
label,
options
}));
console.log(groupedTech);
Normalmente gosto de construir Mappares de chave / valor e, em seguida, transformar essas entradas no resultado final (normalmente usando Array.prototype.map()ou Array.from()).
const tech = [
{ id: 1, grouping: "Front End", value: "HTML" },
{ id: 2, grouping: "Front End", value: "React" },
{ id: 3, grouping: "Back End", value: "Node" },
{ id: 4, grouping: "Back End", value: "PHP" },
];
const groupedMap = tech.reduce((map, { grouping, ...option }) => {
if (!map.has(grouping)) {
map.set(grouping, [])
}
map.get(grouping).push(option)
return map
}, new Map())
const groupedTech = Array.from(groupedMap, ([ label, options ]) => ({
label,
options
}))
console.log(groupedTech)
Usando um mapa e valores de nº de mapa ()
const grouped = tech.reduce((m,{grouping:label, ...rest})=>{
const group = m.get(label) || {label, options:[]};
group.options.push({...rest})
return m.set(label, group)
},new Map)
console.log([...grouped.values()])
<script>
let tech=[{id:1,grouping:"Front End",value:"HTML"},{id:2,grouping:"Front End",value:"React"},{id:3,grouping:"Back End",value:"Node"},{id:4,grouping:"Back End",value:"PHP"}];
</script>