
本文讲解如何将嵌套的日期-分类数据结构(如 category_evolution)重构为“类别为行、日期为列”的响应式 html 表格,解决因直接双层 map 导致的重复行问题,并提供健壮、可扩展的实现方案。
在 React 中渲染跨时间维度的分类汇总表格时,原始数据常以「每个日期包含一组分类条目」的形式存在(即 category_evolution 数组)。若直接对 category_evolution 做两层 map(外层遍历日期、内层遍历该日期下的分类),会生成「每日期独立渲染全部分类」的结果——导致同一类别(如 Stocks)反复出现多行,无法实现横向对比。
要达成目标效果(类别为行、各日期为列),核心思路是:先按 category 归组数据,再为每个类别按日期顺序提取对应值。以下是推荐的实现步骤:
✅ 步骤 1:构建类别 → 日期数据映射表
使用 reduce 将扁平化后的所有条目按 category 聚合,确保每个类别对应一个按原始日期顺序排列的数组:
const combinedData = category_evolution.flatMap(({ data }) => data);
const categoryMap = combinedData.reduce((acc, item) => {
if (!acc[item.category]) {
acc[item.category] = [];
}
acc[item.category].push(item);
return acc;
}, {} as Record); ? 使用 flatMap 替代 reduce(...[...acc, ...cur.data]) 更简洁;类型断言 as Record 提升 TypeScript 安全性。
✅ 步骤 2:动态渲染表头与表体
表头保持不变(日期列由 category_evolution.map 生成),但表体需按唯一类别名迭代,并为每个类别在每一列中取对应日期的数据:
| Categoria | {category_evolution.map(({ name }) => ({name} | ))}
|---|---|
| {category} | {/* 按 category_evolution 的原始顺序,逐个匹配 items 中对应日期的值 */} {category_evolution.map(({ name }) => { const item = items.find(i => i.date === name); return ({item ? item.category_total_brl : '-'} | ); })}
⚠️ 注意事项
- 日期顺序敏感:category_evolution 的顺序决定了表头和数据列的顺序,确保 API 返回或数据预处理时已按时间倒序/正序排好。
- 数据完整性兜底:使用 items.find(...) + 空值判断(如 item ? ... : '-')避免因某日期缺失某类别而报错或渲染 undefined。
- 性能优化:若数据量大(>1000 条),可将 categoryMap 和查找逻辑移至 useMemo 中缓存。
-
扩展性建议:如需支持 USD 列、百分比变化等,只需在
内部扩展渲染逻辑,无需改动整体结构。 此方案解耦了数据结构与 UI 层,既保证语义清晰,又具备良好的可维护性和健壮性,是处理多维聚合表格的典型 React 实践模式。










