Skip to content

aggregate data by more than one value in javascript

I have an array of data where each entry is (year, month, day, field1, field2), e.g:

var d = [
  [2021, 5, 2, 1000, 5000],
  [2021, 5, 3, 2000, 4000],
  [2021, 5, 2, 3000, 4000],
  [2021, 5, 4, 8000, 6000]
  ...
];

I’d like to aggregate this data by date in an efficient way, e.g. producing field1 summed by date (or perhaps some other field depending on user input).

I’m coming from Python where I might use a dict and key by the tuple (year, month, day). I’m having trouble doing something analogous in Javascript. Even with a Map, is there a simple way to key by a combination of year/month/day? Since arrays are compared by reference not value, I’m not sure how to create my keys:

m = new Map();
for (v of d) {
    var k = [v[0], v[1], v[2]];  // no good - unique key every time!
    if (!m.has(k)) {
        m.set(k, 0);
    }
    m.set(k, m.get(k) + v[3]);
}

I can create a string and use that as a key, but at least coming from C++ and Python it feels inefficient to convert 3 numbers to a string. Is there a better pattern for this sort of task in Javascript?

Answer

In JS people usually use JSON.stringify([keys]) as a compound key:

for (let [y, m, d, v] of data) {
    let k = JSON.stringify([y, m, d])
    etc.....
}

A more interesting option would be to attempt to implement tuples. For example, how about this:

class Tuple {
    static cache = new Map

    constructor(...args) {
        let c = Tuple.cache
        for (let a of args)
            c = (c.has(a) || c.set(a, new Map)) && c.get(a)
        return (c.has(null) || c.set(null, args)) && c.get(null)
    }
}

//


let counter = new Map

var data = [
  [2021, 5, 2, 1000, 5000],
  [2021, 5, 3, 2000, 4000],
  [2021, 5, 2, 3000, 4000],
  [2021, 5, 4, 8000, 6000],
  [2021, 5, 2, 3000, 4000],
  [2021, 5, 2, 3000, 4000],
  [2021, 5, 2, 3000, 4000],
  [2021, 5, 2, 3000, 4000],
  
  
];

for (let [y, m, d, v] of data) {
    let k = new Tuple(y, m, d)
    counter.set(k, (counter.get(k) ?? 0) + v)
}

for (let [k, v] of counter)
    console.log(...k, '=', v)