I have the following data:
const data = [
{
id: 1,
metadata: {
attributes: [
{
type: 'background',
value: 'red',
},
{
type: 'background',
value: 'blue',
},
{
type: 'size',
value: 'small',
},
],
},
},
{
id: 2,
metadata: {
attributes: [
{
type: 'background',
value: 'red',
},
{
type: 'background',
value: 'blue',
},
],
},
},
{
id: 3,
metadata: {
attributes: [
{
type: 'background',
value: 'red',
},
{
type: 'background',
value: 'green',
},
{
type: 'size',
value: 'small',
},
],
},
},
];
For each object in the attributes array I have to create a new object based on the type property. The type property will be a key in this new object and the value will be another property nested inside. The value of the nested property will be an array that contains all the corresponding ids. So, I have to achieve something like this as a final result:
const desiredResult = {
background: {
red: [1, 2, 3], //these are ids
blue: [1, 2],
green: [3],
},
size: {
small: [1, 3],
},
};
CodePudding user response:
You can try for each
or for ... of
in javascript. clean code for beginner like below:
function process (data) {
let result = {};
for (let datum of data) {
for (let attribute of datum.metadata.attributes) {
result[attribute.type] = result[attribute.type] || {};
result[attribute.type][attribute.value] = result[attribute.type][attribute.value] || [];
result[attribute.type][attribute.value].push(datum.id);
}
}
return result;
}
//
const desiredResult = process(data);
CodePudding user response:
you can try with a forEach
on the attributes
property inside a reduce
.
Inside the forEach i'm checking if the sub fields (eg background, size) are already created. If not i'm assigning an empty object. Similarly for the values fields inside the type as well. If the value field already exists I'm pushing the id in the array
const data = [ { id: 1, metadata: { attributes: [ { type: 'background', value: 'red', }, { type: 'background', value: 'blue', }, { type: 'size', value: 'small', }, ], }, }, { id: 2, metadata: { attributes: [ { type: 'background', value: 'red', }, { type: 'background', value: 'blue', }, ], }, }, { id: 3, metadata: { attributes: [ { type: 'background', value: 'red', }, { type: 'background', value: 'green', }, { type: 'size', value: 'small', }, ], }, },];
let res = data.reduce((acc,{id,metadata:{attributes}})=>{
attributes.forEach(({type,value}) => {
acc[type] = acc[type]||{}
acc[type][value] = acc[type][value] || []
acc[type][value].push(id)
})
return acc
},{})
console.log(res)
.as-console-wrapper { max-height: 100% !important; top: 0; }
CodePudding user response:
Another approach much like the others here, but using immutable data, even for the accumulator:
const extract = (xs) => xs .reduce ((a, {id, metadata: {attributes = []} = {}}) =>
attributes .reduce ((a, {type: t, value: v}) =>
({...a, [t]: {... (a [t] || {}), [v]: [...((a [t] || {}) [v] || []), id]}}),
a), {})
const data = [{id: 1, metadata: {attributes: [{type: "background", value: "red"}, {type: "background", value: "blue"}, {type: "size", value: "small"}]}}, {id: 2, metadata: {attributes: [{type: "background", value: "red"}, {type: "background", value: "blue"}]}}, {id: 3, metadata: {attributes: [{type: "background", value: "red"}, {type: "background", value: "green"}, {type: "size", value: "small"}]}}]
console .log (extract (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
This works for many sizes of data, but it can run into a performance problem if the data is really large. If you do, then you might have to choose a mutable accumulator instead. But I wouldn't worry about it unless this function shows itself to be a bottleneck in your application.