marielle
marielle

Reputation: 438

Group array of objects by multiple keys using d3.groups

I've this dataset:

const data = [ 
  {animal: 'cat', name: 'mu', date: new Date(2020, 0, 1), status: -1},
  {animal: 'cat', name: 'muji', date: new Date(2021, 0, 1), status: 0},
  {animal: 'cat', name: 'mine', date: new Date(2021, 0, 1), status: 1},
  {animal: 'dog', name: 'fido', date: new Date(2021, 0, 1), status: 1}, 
  {animal: 'dog', name: 'fido2', date: new Date(2020, 0, 1), status: 1}, 
  {animal: 'dog', name: 'fido3', date: new Date(2021, 0, 1), status: 0}, 
  {animal: 'hamster', name: 'gerry', date: new Date(2019, 0, 1), status: 0}, 
  {animal: 't-rex', name: 'dino', date: new Date(2020, 0, 1), status: 0},
  {animal: 't-rex', name: 'sauro', date: new Date(2019, 0, 1), status: 0},
  {animal: 'sheep', name: 's', date: new Date(2019, 0, 1), status: 0}, 
  {animal: 'sheep', name: 'sss', date: new Date(2019, 0, 1), status: -1}, 
]

And I want to group it by animal and by status to obtain something like:

const grouped = {
  cat: {
    -1: [{animal: 'cat', name: 'mu', date: new Date(2020, 0, 1), status: -1}],
    0: [{animal: 'cat', name: 'muji', date: new Date(2021, 0, 1), status: 0}]
    1: [{animal: 'cat', name: 'mine', date: new Date(2021, 0, 1), status: 1}]
  },
  dog: {
    0: [{animal: 'dog', name: 'fido3', date: new Date(2021, 0, 1), status: 0}],
    1: [
      {animal: 'dog', name: 'fido', date: new Date(2021, 0, 1), status: 1}, 
    {animal: 'dog', name: 'fido2', date: new Date(2020, 0, 1), status: 1}
    ]
  },
  hamster: {
    0: [{animal: 'hamster', name: 'gerry', date: new Date(2019, 0, 1), status: 0}],
  },
  't-rex': {
    0: [{animal: 't-rex', name: 'dino', date: new Date(2020, 0, 1), status: 0},
  {animal: 't-rex', name: 'sauro', date: new Date(2019, 0, 1), status: 0},],
  },
  sheep: {
    -1: [{animal: 'sheep', name: 'sss', date: new Date(2019, 0, 1), status: -1}],
    0: [{animal: 'sheep', name: 's', date: new Date(2019, 0, 1), status: 0}],
  }
}

I used d3.groups to groups data:

const grouped = d3.groups(data, d => d.animal, d => d.status)
/*
[
  [
    'cat',
    [
      [
        -1,
        [
          {
            animal: 'cat',
            name: 'mu',
            date: 2019-12-31T23:00:00.000Z,
            status: -1
          }
        ]
      ],
      [
        0,
        [
          {
            animal: 'cat',
            name: 'muji',
            date: 2020-12-31T23:00:00.000Z,
            status: 0
          }
        ]
      ],
      [
        1,
        [
          {
            animal: 'cat',
            name: 'mine',
            date: 2020-12-31T23:00:00.000Z,
            status: 1
          }
        ]
      ]
    ]
  ],
  [
    'dog',
    [
      [
        1,
        [
          {
            animal: 'dog',
            name: 'fido',
            date: 2020-12-31T23:00:00.000Z,
            status: 1
          },
          {
            animal: 'dog',
            name: 'fido2',
            date: 2019-12-31T23:00:00.000Z,
            status: 1
          }
        ]
      ],
      [
        0,
        [
          {
            animal: 'dog',
            name: 'fido3',
            date: 2020-12-31T23:00:00.000Z,
            status: 0
          }
        ]
      ]
    ]
  ],
  [
    'hamster',
    [
      [
        0,
        [
          {
            animal: 'hamster',
            name: 'gerry',
            date: 2018-12-31T23:00:00.000Z,
            status: 0
          }
        ]
      ]
    ]
  ],
  [
    't-rex',
    [
      [
        0,
        [
          {
            animal: 't-rex',
            name: 'dino',
            date: 2019-12-31T23:00:00.000Z,
            status: 0
          },
          {
            animal: 't-rex',
            name: 'sauro',
            date: 2018-12-31T23:00:00.000Z,
            status: 0
          }
        ]
      ]
    ]
  ],
  [
    'sheep',
    [
      [
        0,
        [
          {
            animal: 'sheep',
            name: 's',
            date: 2018-12-31T23:00:00.000Z,
            status: 0
          }
        ]
      ],
      [
        -1,
        [
          {
            animal: 'sheep',
            name: 'sss',
            date: 2018-12-31T23:00:00.000Z,
            status: -1
          }
        ]
      ]
    ]
  ]
]
*/

the grouping is working but data is not in the format I need.

Upvotes: 1

Views: 788

Answers (1)

Robin Mackenzie
Robin Mackenzie

Reputation: 19289

You can chain a .reduce after d3.groups in order that the nested array is recast into a nested object.

You can initialize the reduce with {} so it returns an object. curr[0] for each array returned from d3.groups will be the animal. curr[1] for each array returned from d3.groups will be the array of the status and the original array of items grouped per the animal/ status logic.

See below:

const data = [ 
  {animal: 'cat', name: 'mu', date: new Date(2020, 0, 1), status: -1},
  {animal: 'cat', name: 'muji', date: new Date(2021, 0, 1), status: 0},
  {animal: 'cat', name: 'mine', date: new Date(2021, 0, 1), status: 1},
  {animal: 'dog', name: 'fido', date: new Date(2021, 0, 1), status: 1}, 
  {animal: 'dog', name: 'fido2', date: new Date(2020, 0, 1), status: 1}, 
  {animal: 'dog', name: 'fido3', date: new Date(2021, 0, 1), status: 0}, 
  {animal: 'hamster', name: 'gerry', date: new Date(2019, 0, 1), status: 0}, 
  {animal: 't-rex', name: 'dino', date: new Date(2020, 0, 1), status: 0},
  {animal: 't-rex', name: 'sauro', date: new Date(2019, 0, 1), status: 0},
  {animal: 'sheep', name: 's', date: new Date(2019, 0, 1), status: 0}, 
  {animal: 'sheep', name: 'sss', date: new Date(2019, 0, 1), status: -1}, 
];

const grouped = d3.groups(
  data,
  d => d.animal,
  d => d.status
).reduce((acc, curr) => {
  let obj = {};
  curr[1].forEach(item => obj[item[0]] = item[1]);
  acc[curr[0]] = obj;
  return acc;
}, {});

console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

Upvotes: 2

Related Questions