Skip to content

Instantly share code, notes, and snippets.

@truongluu
Forked from fatihky/multi-level-group-by.js
Created June 17, 2020 03:33
Show Gist options
  • Save truongluu/1c572b97f6a7ab233f9077103c849370 to your computer and use it in GitHub Desktop.
Save truongluu/1c572b97f6a7ab233f9077103c849370 to your computer and use it in GitHub Desktop.
multi level group by, lodash, collections
const _ = require('lodash')
function genrows(groups, groupKey) {
return _.toPairs(groups)
.map(([key, data]) => ({[groupKey]: key, data}))
}
function gengroups(arr, iteratee, key) {
const grouped = _.groupBy(arr, iteratee)
return genrows(grouped, key)
}
/**
* @typedef {Object} GroupByProp
* @prop {String=} key Grouping key
* @prop {String|Function} iteratee An iteratee for Lodash's groupBy
*/
/**
* Group collection's data by multiple iteratees
* @param data
* @param {Array<String|GroupByProp>} props Array of group by objects or property names
* This parameter also can contain both property names and group by objects together
* @returns {Array}
*/
function grouparray(data, props) {
let result = [{data}]
props.forEach((prop, i) => {
const key = prop.key || `k${i + 1}`
const iteratee = prop.iteratee || prop
result = _.flatten(result.map(row => {
return gengroups(row.data, iteratee, key)
.map(group => Object.assign({}, row, {
[key]: group[key],
data: group.data
}))
}))
})
return _.flatten(result)
}
module.exports = grouparray
const assert = require('assert')
const _ = require('lodash')
const d = [ // test data
{x: 1, y: 1, z: 'a'},
{x: 1, y: 1, z: 'b'},
{x: 2, y: 1, z: 'c'},
{x: 2, y: 2, z: 'd'},
{x: 2, y: 3, z: 'e'},
{x: 3, y: 1, z: 'f'}
]
const e = [ // expected result
{k1: 1, k2: 1, data: [{x: 1, y: 1, z: 'a'}, {x: 1, y: 1, z: 'b'}]},
{k1: 2, k2: 1, data: [{x: 2, y: 1, z: 'c'}]},
{k1: 2, k2: 2, data: [{x: 2, y: 2, z: 'd'}]},
{k1: 2, k2: 3, data: [{x: 2, y: 3, z: 'e'}]},
{k1: 3, k2: 1, data: [{x: 3, y: 1, z: 'f'}]}
]
function genrows(groups, groupKey) {
return _.toPairs(groups)
.map(([key, data]) => ({[groupKey]: key, data}))
}
function gengroups(arr, iteratee, key) {
const grouped = _.groupBy(arr, iteratee)
return genrows(grouped, key)
}
function grouparray(data, props) {
let result = [{data}]
props.map((prop, i) => {
const key = prop.key || `k${i + 1}`
const iteratee = prop.iteratee || prop
result = _.flatten(result.map(row => {
return gengroups(row.data, iteratee, key)
.map(group => Object.assign({}, row, {
[key]: group[key],
data: group.data
}))
}))
})
return _.flatten(result)
}
console.log('final', grouparray(d, ['x', 'y']))
// e[0].data[0].x = 274
assert.deepEqual(grouparray(d, ['x', {
iteratee: (data) => data.y
}]), e)
console.log('deepEqual', true)
// console.log('expected full', JSON.stringify(e, null, ' '))
// console.log('final full', JSON.stringify(grouparray(d, ['x', 'y']), null, ' '))
const exptected = [
{
"k1": 1,
"k2": 1,
"data": [
{
"x": 1,
"y": 1,
"z": "a"
},
{
"x": 1,
"y": 1,
"z": "b"
}
]
},
{
"k1": 2,
"k2": 1,
"data": [
{
"x": 2,
"y": 1,
"z": "c"
}
]
},
{
"k1": 2,
"k2": 2,
"data": [
{
"x": 2,
"y": 2,
"z": "d"
}
]
},
{
"k1": 2,
"k2": 3,
"data": [
{
"x": 2,
"y": 3,
"z": "e"
}
]
},
{
"k1": 3,
"k2": 1,
"data": [
{
"x": 3,
"y": 1,
"z": "f"
}
]
}
]
const final = [
{
"data": [
{
"x": 1,
"y": 1,
"z": "a"
},
{
"x": 1,
"y": 1,
"z": "b"
}
],
"k1": "1",
"k2": "1"
},
{
"data": [
{
"x": 2,
"y": 1,
"z": "c"
}
],
"k1": "2",
"k2": "1"
},
{
"data": [
{
"x": 2,
"y": 2,
"z": "d"
}
],
"k1": "2",
"k2": "2"
},
{
"data": [
{
"x": 2,
"y": 3,
"z": "e"
}
],
"k1": "2",
"k2": "3"
},
{
"data": [
{
"x": 3,
"y": 1,
"z": "f"
}
],
"k1": "3",
"k2": "1"
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment