Skip to content

Instantly share code, notes, and snippets.

@dschnare
Last active October 7, 2017 13:51
Show Gist options
  • Save dschnare/f573ae2ec24caceee2dc014fbe5c3c96 to your computer and use it in GitHub Desktop.
Save dschnare/f573ae2ec24caceee2dc014fbe5c3c96 to your computer and use it in GitHub Desktop.
groupBy and collate
/**
* 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 {}
}
}
/**
* 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