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 _.object
1, 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 _.mapObject
2 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.