从嵌套数据结构创建自定义对象数组

时间:2018-05-26 23:24:58

标签: javascript arrays dictionary multidimensional-array reduce

我正在开展一个项目,要求我按下一些API数据(在下面的代码段中显示为'apiData')。我最终需要用于我正在使用的图表库(Recharts)的数据结构是:

[ 
 { date: '2018-04-24', TSLA: 283.37, AAPL: 250.01 },
 { date: '2018-04-25', AAPL: 320.34 } 
]

我已将下面的功能整合在一起并且运行良好,但即使日期之间没有匹配,我也无法显示所有数据。在下面的示例中,您将注意到排除了apiData中日期“2018-04-23”的对象。理想情况下,最终的ds看起来像这样:

[ 
 { date: '2018-04-23', TSLA: 285.12 }
 { date: '2018-04-24', TSLA: 283.37, AAPL: 250.01 },
 { date: '2018-04-25', AAPL: 320.34 } 
]

此外,这可能是一种更高效的方式,但我已经被黑客攻击了一段时间,而目前还没有找到更好的解决方案。例如。随着数据集的增长,forEach并不理想,当我需要绘制长时间的数据时,它就会出现。

所以我的问题是: 1)如何确保在日期中匹配的对象合并而不包含的对象和2)执行此操作的更高效的方法?

如果有人对我的方法有任何意见或批评以及如何改进,我会非常感激。

如果它比下面的代码段更方便,那么这是link to a repl

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)

编辑:我最终使用了内部forEach的charlietfl解决方案,因为我发现这比使用两种reduce方法更容易阅读。最终函数如下所示:

 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());`

3 个答案:

答案 0 :(得分:2)

比每次查找日期循环遍历累积的新数组更简洁的方法是使用一个主日期为主键的对象

以下我使用reduce()返回Map(也可能是对象文字),使用日期作为键,然后将迭代的Map值转换为数组以获得最终结果



const dateMap = apiData.reduce((map,stock)=>{ 
   return stock.chart.reduce((_, chartItem)=>{
      // get stored object for this date, or create new object
      const dayObj = map.get(chartItem.date) || {date: chartItem.date};
      dayObj[stock.quote.symbol] = chartItem.close;
      // store the object in map again using date as key
      return map.set(chartItem.date, dayObj);
   },map);  
}, new Map)

const res = [...dateMap.values()];

console.log(res)

.as-console-wrapper {max-height: 100%!important;}

<script>
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"
    },
  }
]


</script>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

只是纠正你的代码,否则应该使用reduce而不是map,对于图表也是如此。

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, i) => {
            if (arrItem.date === item.date) {
              chartObj = { ...arrItem, ...chartObj };
              delete(arr[i]);
            }
          });
        }
        return chartObj;
      });

      return [...arr, ...stockArr].filter(e=>!!e); //to remove undefined elements left by delete above.
    }, []);

  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)

答案 2 :(得分:0)

问题是您的reduce函数正在为数据数组中的每个项目运行。当它在数据数组中的第一个项目上运行时,它返回:

[
    { date: '2018-04-23', TSLA: 285.12 },
    { date: '2018-04-24', TSLA: 283.37 }
]

当它在数组中的第二个项目上运行时,它返回:

[
    { date: '2018-04-24', TSLA: 283.37, AAPL: 250.01 },
    { date: '2018-04-25', AAPL: 320.34 } 
]

这是因为当reduce在最后一个数组项上运行时,它将从该项返回结果。您只是合并累加器变量中的项目&#34; arr&#34;如果他们的日期也在当前数组项中。由于2018-04-23是第一个但不是第二个,它没有被添加。我在代码中添加了两个东西。首先,如果循环的当前日期是在累加器变量&#34; arr&#34;我从&#34; arr&#34;删除它合并之后。第二个更改是在每个.reduce循环之后仍然有一些日期在&#34; arr&#34;在当前&#34; stockArr&#34;中并不存在。为了解决这个问题,我合并了两个&#34; arr&#34;和&#34; stockArr&#34;它会给你你想要的东西。

&#13;
&#13;
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, index) => {
            if (arrItem.date === item.date) {
              chartObj = { ...arrItem, ...chartObj };
              arr.splice(index, 1);
            }
          });
        }
        return chartObj;
      });
      return [...arr, ...stockArr];
    }, []);

  console.log(chartData)
}

const data = [
  {
    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(data)
&#13;
&#13;
&#13;