0

I have following data:

var groups = {
  0: { 'GroupID': 1, 'AttributeID': 'Agitator', 'Value': 'No' },
  1: { 'GroupID': 1, 'AttributeID': 'ATG', 'Value': 'Custodian' },
  2: { 'GroupID': 1, 'AttributeID': 'Heating Coil', 'Value': 'Mild Steel' },
  3: { 'GroupID': 2, 'AttributeID': 'Agitator', 'Value': 'Yes' },
  4: { 'GroupID': 2, 'AttributeID': 'ATG', 'Value': 'Non Custodian' },
  5: { 'GroupID': 2, 'AttributeID': 'Heating Coil', 'Value': 'Mild Steel' },
};

I would like to group the data by GroupID and use one of the value as key for another value. The expected outcome will be as following. Is this achievable?

var data = {
  0: { 'GroupID': 1, 'Agitator': 'No', 'ATG': 'Custodian', 'Heating Coil': 'Mild Steel' },
  1: { 'GroupID': 2, 'Agitator': 'Yes', 'ATG': 'Non Custodian', 'Heating Coil': 'Mild Steel' }
};

So far what I have tried is as below. I have no idea how to make one of value as key for another value.

_.chain(groups).groupBy('GroupID').map((value, key) => {
    return {
      GroupID: key,
      AttributeID: _.map(value, 'AttributeID'),
      Value: _.map(value, 'Value')
    }
}).value();

The result looks like below.

var data = {
      0: { 'GroupID': 1, 'AttributeID': ['Agitator', 'ATG', 'Heating Coil'], 'Value': ['No', 'Custodian', 'Mild Steel'] },
      1: { 'GroupID': 2, 'AttributeID': ['Agitator', 'ATG', 'Heating Coil'], 'Value': ['Yes', 'Non Custodian', 'Mild Steel'] }
    };

Appreciate any help.

grg
  • 5,023
  • 3
  • 34
  • 50
  • Have you made any attempt at solving your own question, and if so, what did it look like? What sources have you consulted? Is there any documentation that confused you? – Julian Nov 19 '20 at 23:25
  • Edited my question to include what I have tried so far. Thanks. – Nicole Chua Nov 20 '20 at 02:00

1 Answers1

0

You got very close to solving your own question. Well done! Let's break this into parts and see what's missing.

You correctly identified that _.groupBy is what you need in order to collect objects with the same GroupID.

var grouped = _.groupBy(groups, 'GroupID');
// { '1':
//    [ { GroupID: 1, AttributeID: 'Agitator', Value: 'No' },
//      { GroupID: 1, AttributeID: 'ATG', Value: 'Custodian' },
//      { GroupID: 1, AttributeID: 'Heating Coil', Value: 'Mild Steel' } ],
//   '2':
//    [ { GroupID: 2, AttributeID: 'Agitator', Value: 'Yes' },
//      { GroupID: 2, AttributeID: 'ATG', Value: 'Non Custodian' },
//      { GroupID: 2, AttributeID: 'Heating Coil', Value: 'Mild Steel' } ] }

You also correctly identified _.map as the function that lets you collect the values of a particular property across an array of objects.

var firstGroupAttributes = _.map(grouped['1'], 'AttributeID');
// [ 'Agitator', 'ATG', 'Heating Coil' ]

The function you wrote to construct the final object for each group produced the following object for the first group:

{
    'GroupID': 1,
    'AttributeID': ['Agitator', 'ATG', 'Heating Coil'],
    'Value': ['No', 'Custodian', 'Mild Steel']
}

Conveniently, the AttributeID and Value properties have the same order, so the first element of the Value array corresponds to the first element of the AttributeID array, the second to the second, and so forth. Underscore has a function called _.object1, which can take such a pair of arrays and return an object where each element of the first array names a property and the corresponding element of the second array provides its value:

_.object(['Agitator', 'ATG'], ['No', 'Custodian']);
// { Agitator: 'No',
//   ATG: 'Custodian', }

Having taken care of the attributes and their values, now we just need to add the GroupID to the object. There are several similar but slightly different functions that work for this purpose, such as _.extend and _.defaults:

_.extend({ a: 1 }, { b: 2 });
// { a: 1, b: 2 }

Putting these ingredients together, the function that we map over grouped could look something like this:

function mergeGroup(value, key) {
    var attributes = _.map(value, 'AttributeID');
    var values = _.map(value, 'Value');
    return _.chain(attributes).object(values).extend({ GroupID: key }).value();
}

If you use _.map, as you did, you get an array with the final objects.

var data = _.chain(groups).groupBy('GroupID').map(mergeGroup).value();
// [ { Agitator: 'No',
//     ATG: 'Custodian',
//     'Heating Coil': 'Mild Steel',
//     GroupID: '1' },
//   { Agitator: 'Yes',
//     ATG: 'Non Custodian',
//     'Heating Coil': 'Mild Steel',
//     GroupID: '2' } ]

Alternatively, you could use _.mapObject2 to produce an object where each key is the same as the GroupID:

var data = _.chain(groups).groupBy('GroupID').mapObject(mergeGroup).value();
// { '1':
//    { Agitator: 'No',
//      ATG: 'Custodian',
//      'Heating Coil': 'Mild Steel',
//      GroupID: '1' },
//   '2':
//    { Agitator: 'Yes',
//      ATG: 'Non Custodian',
//      'Heating Coil': 'Mild Steel',
//      GroupID: '2' } }

1 Lodash originally inherited _.object from Underscore, but they added an alias called _.zipObject and in version 4, they decided to remove the original name _.object from their interface. So you'd have to call _.zipObject if using Lodash. Underscore's _.object can also work with an array of pairs instead of a pair of arrays; in Lodash 4, you'd use _.fromPairs for that instead.

2 _.mapValues in Lodash.

Julian
  • 4,176
  • 19
  • 40
  • Hi Julian, thank you so much for your help and spending time to explain your solution. It works like a charm! Appreciated! – Nicole Chua Nov 23 '20 at 08:30