Last active
October 7, 2017 13:51
-
-
Save dschnare/f573ae2ec24caceee2dc014fbe5c3c96 to your computer and use it in GitHub Desktop.
groupBy and collate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Collates all properties with varying values or the specified properties in | |
* the property map into a single object. | |
* | |
* If the propMap is an Object then the keys are the properties to collate and | |
* the values are the target property name to created on the collated object. If | |
* the key in the propMap is a comma delimited string then the target property | |
* will be set to an object with all the properties in the comma delimited | |
* key. | |
* | |
* Alternatively, the propMap can be an array of property names to be collated | |
* into arrays. The target property will be the same as the property being | |
* collated. | |
* | |
* If no propMap is specified then all varying properties will be collated into | |
* arrays and set to the same property on the collated object. | |
* | |
* @example | |
* collate([ | |
* { id: 3, name: 'Darren', friendName: 'Alex', age: 20 }, | |
* { id: 3, name: 'Darren', friendName: 'Julia', age: 30 }, | |
* { id: 3, name: 'Darren', friendName: 'Jon', age: 40 }, | |
* { id: 3, name: 'Darren', friendName: 'Jesse', age: 50 }, | |
* { id: 3, name: 'Darren', friendName: 'Mat', age: 60 } | |
* ]) // { id: 3, name: 'Darren', age: 20, 'friendName': [ 'Alex, 'Julia', ... ] } | |
* @example | |
* collate([ | |
* { id: 3, name: 'Darren', friendName: 'Alex', age: 20 }, | |
* { id: 3, name: 'Darren', friendName: 'Julia', age: 30 }, | |
* { id: 3, name: 'Darren', friendName: 'Jon', age: 40 }, | |
* { id: 3, name: 'Darren', friendName: 'Jesse', age: 50 }, | |
* { id: 3, name: 'Darren', friendName: 'Mat', age: 60 } | |
* ], { friendName: 'friendNames' }) // { id: 3, name: 'Darren', age: 20, 'friendNames': [ ... ] } | |
* @example | |
* collate([ | |
* { id: 3, name: 'Darren', friendName: 'Alex', age: 20 }, | |
* { id: 3, name: 'Darren', friendName: 'Julia', age: 30 }, | |
* { id: 3, name: 'Darren', friendName: 'Jon', age: 40 }, | |
* { id: 3, name: 'Darren', friendName: 'Jesse', age: 50 }, | |
* { id: 3, name: 'Darren', friendName: 'Mat', age: 60 } | |
* ], { 'friendName,age': 'friends' }) // { id: 3, name: 'Darren', 'friends': [ { friendName: 'Alex', age: 20 }, ... ] } | |
* @param {Object[]} items List of items to have properties collated | |
* @param {Object | string[]} [propMap] The properties to collate | |
* @return {Object} The collated object | |
*/ | |
export default function collate (items, propMap = null) { | |
let props = [] | |
if (propMap) { | |
if (Array.isArray(propMap)) { | |
props = propMap.slice() | |
propMap = props.reduce((map, prop) => { | |
return Object.assign(map, { [prop]: prop }) | |
}, {}) | |
} else { | |
props = Object.keys(propMap) | |
} | |
} else { | |
const first = items[0] || {} | |
const last = items[items.length - 1] || {} | |
props = Object.keys(first).reduce((props, prop) => { | |
return first[prop] === last[prop] ? props : props.concat(prop) | |
}, []) | |
propMap = props.reduce((map, prop) => { | |
return Object.assign(map, { [prop]: prop }) | |
}, {}) | |
} | |
if (items.length > 0) { | |
return props.reduce((collatedItem, prop) => { | |
const props = prop.split(',') | |
const mappedProp = propMap[prop] | |
collatedItem = Object.assign({}, collatedItem, { | |
[mappedProp]: props.length === 1 | |
? items.reduce((a, it) => { | |
return a.concat(it[prop]) | |
}, []).filter(v => v !== null && v !== undefined) | |
: items.reduce((a, it) => { | |
return a.concat( | |
props.reduce((o, prop) => { | |
return Object.assign({}, o, { | |
[prop]: it[prop] | |
}) | |
}, {}) | |
) | |
}, []).filter(o => { | |
// This removes all objects that any of the peropties set to null or | |
// undefined. Can't remember why this is here, seems to me that this | |
// wouldn't be that useful since you'd likely want to keep undefined | |
// properties. This occurs if items in the item array don't have | |
// values for all the collated properites. | |
return props.every(p => o[p] !== null && o[p] !== undefined) | |
}) | |
}) | |
props.filter(prop => { | |
return prop !== mappedProp | |
}).forEach(prop => { | |
delete collatedItem[prop] | |
}) | |
return collatedItem | |
}, items[0]) | |
} else { | |
return {} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Groups an array of items by the specified property. | |
* @example | |
* groupBy([ | |
* { id: 3, name: 'Darren', friendName: 'Alex', age: 20 }, | |
* { id: 3, name: 'Darren', friendName: 'Julia', age: 20 }, | |
* { id: 3, name: 'Darren', friendName: 'Jon', age: 20 }, | |
* { id: 3, name: 'Darren', friendName: 'Jesse', age: 20 }, | |
* { id: 3, name: 'Darren', friendName: 'Mat', age: 20 } | |
* ], 'id') // [[ { id: 3, name: 'Darren', friendName: 'Alex', age: 20 }, { ... } ]] | |
* @param {Object[]} items The list of items to group | |
* @param {string} groupBy The property to group on | |
* @return {Array<any[]>} The list of groups | |
*/ | |
export default function groupBy (items, groupBy) { | |
return items.reduce((list, item) => { | |
const value = item[groupBy] | |
if (list.some(items => items.some(it => it[groupBy] === value))) { | |
return list | |
} else { | |
list.push( | |
items.filter(it => it[groupBy] === value) | |
) | |
return list | |
} | |
}, []) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment