Bryan Veloso
Bryan Veloso

Reputation: 1595

Grouping Nested Objects with Dynamic Keys (Using Lodash)

I'm querying Firebase to get some data to throw into Chart.js. Here's how I've laid out my data:

{
  "20160428": {
    "follow": 13,
    "host": 6,
    "raid": 1,
    "substreak": 1,
    "tip": 1
  },
  "20160429": {
    "follow": 15,
    "host": 21,
    "raid": 2,
    "substreak": 4
  },
  "20160430": {
    "follow": 4
  },
  "20160501": {
    "follow": 11,
    "host": 15,
    "subscription": 4,
    "substreak": 5
  },
  "20160502": {
    "follow": 2,
    "host": 6,
    "subscription": 1,
    "substreak": 4
  },
  "20160503": {
    "follow": 2
  }
}

As you can see, each object is keyed off by a timestamp and events don't always appear in every object (but there are a finite number of events). Here's how I'd like the data to look so I can feed it into Chart.js:

labels: ["20160428", "20160429", "20160430", ...]

{
  "follow": [13, 15, 4, 11, 2, 2],
  "host": [6, 21, 0, 15, 6, 0],
  "raid": [1, 2, 0, 0, 0, 0],
  "subscription": [0, 0, 0, 4, 1, 0]
  "substreak": [1, 4, 0, 5, 4, 0]
  "tip": [1, 0, 0, 0, 0, 0]
}

I've played with Lodash's groupBy and similar functions, but I'm not sure if I'm on the right track. I wouldn't mind doing this x times either per event, but at this point I can't change the schema.

Upvotes: 3

Views: 3503

Answers (3)

ryeballar
ryeballar

Reputation: 30098

If you have a defined set of keys that you want to group then you have to:

  1. Use map() to pluck values with compact() to get non-null values from the object collection from the defined set of keys.

  2. Build the result using zipObject() from the defined keys and values obtained from the first step.


var keys = ["follow", "host", "raid", "substreak", "tip", "subscription"];
var values = _.map(keys, key => _(data).map(key).compact().value());
var result = _.zipObject(keys, values);

var data = {
  "20160428": { "follow": 13, "host": 6, "raid": 1, "substreak": 1, "tip": 1 },
  "20160429": { "follow": 15, "host": 21, "raid": 2, "substreak": 4 },
  "20160430": { "follow": 4 },
  "20160501": { "follow": 11, "host": 15, "subscription": 4, "substreak": 5 },
  "20160502": { "follow": 2, "host": 6, "subscription": 1, "substreak": 4 },
  "20160503": { "follow": 2 }
};

var keys = ["follow", "host", "raid", "substreak", "tip", "subscription"];
var values = _.map(keys, key => _(data).map(key).compact().value());
var result = _.zipObject(keys, values);

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js"></script>

If you want to group them from all the keys present in the object collection then you can:

  1. Get all the unique keys by:

    • map() each data object inside the object collection
    • flatten() the resulting array
    • Use uniq() to get all unique keys from the flattened array.
  2. Use the methodology in the first example to get the values and build the object.


var keys = _(data).map(_.keys).flatten().uniq().value();
var values = _.map(keys, key => _(data).map(key).compact().value());
var result = _.zipObject(keys, values);

var data = {
  "20160428": { "follow": 13, "host": 6, "raid": 1, "substreak": 1, "tip": 1 },
  "20160429": { "follow": 15, "host": 21, "raid": 2, "substreak": 4 },
  "20160430": { "follow": 4 },
  "20160501": { "follow": 11, "host": 15, "subscription": 4, "substreak": 5 },
  "20160502": { "follow": 2, "host": 6, "subscription": 1, "substreak": 4 },
  "20160503": { "follow": 2 }
};

var keys = _(data).map(_.keys).flatten().uniq().value();
var values = _.map(keys, key => _(data).map(key).compact().value());
var result = _.zipObject(keys, values);

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js"></script>

Upvotes: 2

Don't try this at home.

var fields = {
  follow: 0,
  host: 0,
  raid: 0,
  substreak: 0,
  tip: 0,
  subscription: 0
};

_(data)
  .values()
  .map(x => _.assign({}, fields, x))
  .map(_.toPairs)
  .flatten()
  .groupBy(0)
  .mapValues(x => _.map(x, 1))
  .value();

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386654

You could use plain javascript and some loops.

var data = { "20160428": { "follow": 13, "host": 6, "raid": 1, "substreak": 1, "tip": 1 }, "20160429": { "follow": 15, "host": 21, "raid": 2, "substreak": 4 }, "20160430": { "follow": 4 }, "20160501": { "follow": 11, "host": 15, "subscription": 4, "substreak": 5 }, "20160502": { "follow": 2, "host": 6, "subscription": 1, "substreak": 4 }, "20160503": { "follow": 2 } },
    grouped = {}

Object.keys(data).forEach(function (k) {
    ["follow", "host", "raid", "substreak", "tip"].forEach(function (kk) {
        grouped[kk] = grouped[kk] || [];
        grouped[kk].push(data[k][kk] || 0);
    });
});

document.write('<pre>' + JSON.stringify(grouped, 0, 4) + '</pre>');

Upvotes: 1

Related Questions