3

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.

3 Answers 3

2

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>

1

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>');

3
  • You could replace the hard-coded properties array with Object.keys(data[k]) in your second forEach to allow your solution to work for an arbitrary set of properties.
    – Michael L.
    Commented May 3, 2016 at 19:35
  • @MichaelL., if the array is not hard-coded, then the arrays may have not the same length. Commented May 3, 2016 at 19:53
  • Nothing in your code guarantees a specific array length. Dynamically creating the array only makes the code more flexible and maintainable. See jsfiddle.
    – Michael L.
    Commented May 3, 2016 at 21:38
1

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();

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.