You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用d3.groups和JS reduce替换依赖d3.nest的旧D3.js(v7)代码

将D3.js v7中的d3.nest替代方案实现嵌套对象

问题背景

原D3旧版本使用d3.nest()实现数据嵌套,代码如下:

d3
  .nest()
  .key(function (d) {
    return d.year;
  })
  .key(function (d) {
    return d.mw;
  })
  .key(function (d) {
    return d.variant;
  })
  .map(csv)

升级到D3 v7后,d3.nest已被移除,需要用d3.groups结合JS原生方法实现相同的嵌套对象结构。目标是将如下原始数据:

const dummy = [
  { variant: "0", year: "1950", mw: "m", pop: "31962", pop_0_4: "540" },
  { variant: "0", year: "1950", mw: "w", pop: "32052", pop_0_4: "545" },
  { variant: "0", year: "1951", mw: "m", pop: "31453", pop_0_4: "520" },
  { variant: "0", year: "1951", mw: "w", pop: "35444", pop_0_4: "526" },
  { variant: "1", year: "1950", mw: "m", pop: "41253", pop_0_4: "510" },
  { variant: "1", year: "1950", mw: "w", pop: "42356", pop_0_4: "515" },
  { variant: "1", year: "1951", mw: "m", pop: "45633", pop_0_4: "500" },
  { variant: "1", year: "1951", mw: "w", pop: "49500", pop_0_4: "501" },
];

转换为可通过desired['1950'].m['0'][0].pop_0_4取值的嵌套对象结构。

注意:你提供的desired对象存在重复键(同一个1950/1951出现两次),这在JS对象中是不合法的,后面的键会覆盖前面的。实际正确的嵌套结构应该是每个年份下包含所有对应的mw和variant分组。

用户尝试了d3.groups和reduce,但未能完全将嵌套数组转为对象:

// 用户尝试代码
let test;

test = d3
  .groups(
    dummy,
    (d) => d.year,
    (d) => d.mw,
    (d) => d.variant
  )
  .reduce((acc, curr) => {
    let obj = {};
    curr[1].forEach((item) => (obj[item[0]] = item[1]));
    acc[curr[0]] = obj;
    return acc;
  }, {});

解决方案

d3.groups返回的是[[key1, [[key2, [[key3, dataArray]]]]]]的嵌套数组结构,每一级都是[分组键, 下一级分组/数据数组]的形式。需要将这种数组结构逐层转换为对象属性,以下是两种可行实现方式:

方式一:递归转换函数

编写通用递归函数,适配任意层级的分组转换:

function groupsToObject(groups) {
  return groups.reduce((acc, [key, value]) => {
    // 判断下一级是否仍为分组结构,递归转换
    if (Array.isArray(value[0]) && typeof value[0][0] === 'string') {
      acc[key] = groupsToObject(value);
    } else {
      // 最底层为数据数组,直接赋值
      acc[key] = value;
    }
    return acc;
  }, {});
}

// 使用方式
const desired = groupsToObject(
  d3.groups(
    dummy,
    d => d.year,
    d => d.mw,
    d => d.variant
  )
);

// 验证取值
console.log(desired['1950'].m['0'][0].pop_0_4); // 输出 "540"
console.log(desired['1950'].m['1'][0].pop_0_4); // 输出 "510"

方式二:逐层手动处理

如果分组层级固定,可直接逐层解构转换,避免递归:

const desired = d3.groups(dummy, d => d.year, d => d.mw, d => d.variant)
  .reduce((yearAcc, [year, mwGroups]) => {
    yearAcc[year] = mwGroups.reduce((mwAcc, [mw, variantGroups]) => {
      mwAcc[mw] = variantGroups.reduce((variantAcc, [variant, data]) => {
        variantAcc[variant] = data;
        return variantAcc;
      }, {});
      return mwAcc;
    }, {});
    return yearAcc;
  }, {});

// 验证取值
console.log(desired['1951'].w['1'][0].pop_0_4); // 输出 "501"

内容的提问来源于stack exchange,提问作者PaoloRossi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 17:45:01