I'm working on a project that requires me to massage some API data (shown in the snippet below as 'apiData'). The data structure I ultimately need for the charting library I'm using (Recharts) is this:
[
{ date: '2018-04-24', TSLA: 283.37, AAPL: 250.01 },
{ date: '2018-04-25', AAPL: 320.34 }
]
I've put together the function below and it works well enough, but I'm having trouble getting all the data to show up, even if there's no match between dates. In the below example, you'll notice that the object for date "2018-04-23" in the apiData is excluded. Ideally, the final ds would look like this:
[
{ date: '2018-04-23', TSLA: 285.12 }
{ date: '2018-04-24', TSLA: 283.37, AAPL: 250.01 },
{ date: '2018-04-25', AAPL: 320.34 }
]
Also, there's probably a more performant way to do this, but I've been hacking away for a while and not seeing a better solution at the moment. E.g. the forEach isn't ideal as the data set grows, which it will when I need to plot long time periods of data.
So my questions are: 1) How can I make sure objects that match in date are combined while objects that don't are still included and 2) what's a more performant way to do this operation?
If anyone has any input or critique of my approach and how I can improve it, I'd greatly appreciate it.
Here's a link to a repl if it's more convenient then the code snippet below.
formatChartData = (data) => {
const chartData = data
.reduce((arr, stock) => {
const stockArr = stock.chart.map((item) => {
let chartObj = {};
chartObj.date = item.date;
chartObj[stock.quote.symbol] = item.close;
if (arr.length > 0) {
arr.forEach((arrItem) => {
if (arrItem.date === item.date) {
chartObj = { ...arrItem, ...chartObj };
}
});
}
return chartObj;
});
return stockArr;
}, []);
console.log(chartData)
}
const apiData = [
{
chart: [
{
date: "2018-04-23",
open: 291.29,
close: 285.12,
},
{
date: "2018-04-24",
open: 291.29,
close: 283.37,
},
],
news: [],
quote: {
symbol: "TSLA"
},
},
{
chart: [
{
date: "2018-04-24",
open: 200.29,
close: 250.01,
},
{
date: "2018-04-25",
open: 290.20,
close: 320.34,
},
],
news: [],
quote: {
symbol: "AAPL"
},
}
]
formatChartData(apiData)
EDIT: I ended up using charlietfl's solution with an inner forEach as I found this easier to read than using two reduce methods. The final function looks like this:
const chartData = data
.reduce((map, stock) => {
stock.chart.forEach((chart) => {
const chartObj = map.get(chart.date) || { date: chart.date };
chartObj[stock.quote.symbol] = chart.close;
map.set(chart.date, chartObj);
});
return map;
}, new Map());`