从嵌套数据结构生成自定义对象数组的优化方案
问题分析
你遇到的两个问题本质上是原实现的逻辑缺陷与低效遍历方式导致的:
- 遗漏无匹配日期:原
forEach方案大概率是以某一个品类的日期为基准生成结果,这会直接忽略其他品类独有的日期;同时填充品类值时,没有处理「该日期下某品类无数据」的场景,甚至直接跳过这类日期的创建。 - 性能瓶颈:嵌套
forEach加上find查找,会让时间复杂度达到O(M*N)(M是品类数,N是每个品类的平均数据点数),数据量增大时性能会急剧下降。
解决方案思路
针对这两个问题,我们可以用Map数据结构重构逻辑,核心思路是:
- 用Map存储「日期字符串 → 对应数据点对象」的映射,确保所有出现过的日期都被捕获;
- 遍历所有品类的所有数据点,逐个填充到Map对应的日期对象中,避免嵌套查找;
- 最终将Map的values转换为数组,得到完整的目标数据。
优化后的代码实现
假设你的apiData结构大致如下(结构不同可微调逻辑):
const apiData = { AAPL: [ { date: '2018-04-22', value: 100 }, { date: '2018-04-23', value: 105 } ], TSLA: [ { date: '2018-04-23', value: 200 }, { date: '2018-04-24', value: 210 } ] };
下面是优化后的formatChartData函数:
function formatChartData(apiData) { // 提取所有品类名称,用于初始化数据点的结构 const tickers = Object.keys(apiData); // 用Map存储每个日期对应的数据点,保证日期唯一性与快速查找 const dateDataMap = new Map(); // 遍历每个品类的所有数据条目 for (const ticker of tickers) { const entries = apiData[ticker]; for (const { date, value } of entries) { // 如果当前日期未在Map中,初始化包含所有品类默认值的数据点 if (!dateDataMap.has(date)) { const initialPoint = { date }; // 给所有品类设置默认值(这里用null,可按需改成0或其他值) tickers.forEach(t => initialPoint[t] = null); dateDataMap.set(date, initialPoint); } // 更新当前品类在该日期下的数值 dateDataMap.get(date)[ticker] = value; } } // 将Map的值转为数组,并按日期排序(可选,根据图表需求决定是否保留) return Array.from(dateDataMap.values()).sort((a, b) => new Date(a.date) - new Date(b.date)); }
方案优势说明
- 保留所有日期:只要任何一个品类包含某个日期,该日期就会被加入Map,其他品类在该日期下的值会被设为默认值,彻底解决了TSLA 2018-04-23这类数据被遗漏的问题。
- 性能大幅提升:整个逻辑的时间复杂度是O(N)(N是所有品类的数据点总数),Map的
get/set操作都是O(1),相比原方案的O(M*N),数据量越大性能优势越明显。 - 逻辑清晰易维护:每个数据点只被处理一次,没有嵌套查找操作,代码可读性和可维护性更好。
可选调整
如果图表要求「无数据的品类显示0而非null」,只需把初始化默认值的null改成0即可:
tickers.forEach(t => initialPoint[t] = 0);
内容的提问来源于stack exchange,提问作者abohannon
相关产品推荐
相关产品推荐

