skip to Main Content

I’m trying to update some older D3js code that relies on d3.nest to the newest version (v7). This is an example of the raw data (from a csv) and the desired nested Object that I’m trying to achieve. Previously, this step relied on d3.nest

import * as d3 from "https://cdn.jsdelivr.net/npm/[email protected]/+esm";

var dummy;
var desired;

// example data from csv
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" },
];
console.log(dummy);

// desired format is an object I can reference using eg desired[1950].m[0][0]["pop_0_4"]
desired = {
  1950: {
    m: {
      0: [
        { variant: "0", year: "1950", mw: "m", pop: "31962", pop_0_4: "540" },
      ],
    },
    w: {
      0: [
        { variant: "0", year: "1950", mw: "m", pop: "32052", pop_0_4: "545" },
      ],
    },
  },
  1951: {
    m: {
      0: [
        { variant: "0", year: "1951", mw: "m", pop: "31453", pop_0_4: "520" },
      ],
    },
    w: {
      0: [
        { variant: "0", year: "1951", mw: "m", pop: "35444", pop_0_4: "526" },
      ],
    },
  },
  1950: {
    m: {
      0: [
        { variant: "1", year: "1950", mw: "m", pop: "41253", pop_0_4: "510" },
      ],
    },
    w: {
      0: [
        { variant: "1", year: "1950", mw: "m", pop: "42356", pop_0_4: "515" },
      ],
    },
  },
  1951: {
    m: {
      0: [
        { variant: "1", year: "1951", mw: "m", pop: "45633", pop_0_4: "500" },
      ],
    },
    w: {
      0: [
        { variant: "1", year: "1951", mw: "m", pop: "49500", pop_0_4: "501" },
      ],
    },
  },
};
console.log(desired);
console.log(desired[1950].m[0][0]["pop_0_4"]);

// In the old code this was done using
d3
  .nest()
  .key(function (d) {
    return d.year;
  })
  .key(function (d) {
    return d.mw;
  })
  .key(function (d) {
    return d.variant;
  })
  .map(csv)

This is where I’ve got to using d3.groups and js reduce but I can’t work out how to convert the remaining nested arrays to objects. I want to be able to reference values in the final object as eg desired[1950].m[0][0]["pop_0_4"]

// my attempt - I can get close using d3.groups and reduce
// but I can't work out how to convert the remaining nested arrays to objects
var 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;
  }, {});
console.log(test);

2

Answers


  1. I recommend that you use d3.group, rather than d3.groups, which returns nested Map objects, rather than nested arrays. As explained in this section of the MDN documentation, Maps are very much like Objects but often preferable in the context of data storage and access.

    Thus, you might try

    grouped = d3.group(
      dummy,
      (d) => d.year,
      (d) => d.mw,
      (d) => d.variant
    )
    

    You could then access the data like so:

    grouped.get("1950").get("m").get("0")[0].pop
    

    Better yet, you could use d3.rollup, which is a lot like d3.group but provides a reducer to the final result. That would allow you to remove the superfluous [0] in your accessor. Thus:

    rolled = d3.rollup(
      dummy,
      (A) => A[0],
      (d) => d.year,
      (d) => d.mw,
      (d) => d.variant
    )
    

    Then,

    rolled.get("1950").get("m").get("1").pop
    
    Login or Signup to reply.
  2. You can use vanilla to reach your goal too.

    const sourceData = [
      { 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" },
    ];
    const desired = {};
    
    sourceData.forEach(entry => {
      const year = entry.year;
      const mw = entry.mw;
      const variant = entry.variant;
    
      if (!desired[year]) {
        desired[year] = {};
      }
    
      if (!desired[year][mw]) {
        desired[year][mw] = {};
      }
    
      if (!desired[year][mw][variant]) {
        desired[year][mw][variant] = [];
      }
    
      desired[year][mw][variant].push(entry);
    });
    
    
    console.log(desired[1950].m[0][0]["pop_0_4"]);
    console.log(desired); // The transformed data
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search