I have the following arrays of objects, for example:
const data = [ { date: '01-01', products: [ { id: 1, value: 6, label: 'Product 1' }, { id: 2, value: 3, label: 'Product 2' } ] }, { date: '02-01', products: [ { id: 1, value: 4, label: 'Product 1' }, ] }, { date: '03-01', products: [ { id: 1, value: 11, label: 'Product 1' }, { id: 2, value: 15, label: 'Product 2' } ] } ]
Then I do the grouping and get the following result:
const output = [ { id: 1, name: 'Product 1', data: [6, 4, 11] }, { id: 2, name: 'Product 2', data: [3, 15] } ]
The problem with the solution is that I cannot take into account the missing value (the object with the date “02-01” does not have an object with id: 2). I need to check that the object does not exist and substitute zero instead of the missing value. Maybe you know how to do it?
Solution code below:
const result = data.map(e => e.products).flat().reduce((acc, product) => { const index = acc.findIndex(item => item.id === product.id); if(index === -1) { acc.push({ id: product.id, name: product.label, data: [product.value] }) return acc; } const findIndex = acc[index].data.findIndex((innerNode) => innerNode.id === product.id); if (findIndex === -1) { console.log(product.value) acc[index].data.push(product.value); return acc; } return acc; }, []);
Expected result:
const output = [ { id: 1, name: 'Product 1', data: [6, 4, 11] }, { id: 2, name: 'Product 2', data: [3, 0, 15] } ]
Advertisement
Answer
You can do this in three passes:
- first, you find all dates. When you first encounter a product, you will set all its values to 0 for each of those dates.
- then, you iterate products and ensure that, for each date, they have a value – which will be zero by default.
- finally, you format the output.
const data = [ { date: '01-01', products: [ { id: 1, value: 6, label: 'Product 1' }, { id: 2, value: 3, label: 'Product 2' } ] }, { date: '02-01', products: [ { id: 1, value: 4, label: 'Product 1' }, ] }, { date: '03-01', products: [ { id: 1, value: 11, label: 'Product 1' }, { id: 2, value: 15, label: 'Product 2' } ] } ] // goal is to fill this for each product let dateToValues = data.map(d => [d.date, 0]); // build map of product-id to values-for-each-date let products = new Map(); data.forEach(d => d.products.forEach(p => { let values = products.get(p.id)?.data; if (values === undefined) { values = new Map(dateToValues); // a copy products.set(p.id, {label: p.label, data: values}); } values.set(d.date, p.value); })) // generate output, skipping dates and only showing their values let output = []; products.forEach((v, id) => output.push({ id: id, name: v.label, data: [... v.data.values()]})); console.log(output)