Skip to content

Instantly share code, notes, and snippets.

@SynCap
Last active December 28, 2020 11:06
Show Gist options
  • Save SynCap/e7bc200589551ebc6760d1db68c8b56e to your computer and use it in GitHub Desktop.
Save SynCap/e7bc200589551ebc6760d1db68c8b56e to your computer and use it in GitHub Desktop.
Real GROUP BY in JavaScript (ES2015)

GROUP BY with JavaScript

rmProp(obj: Object, prop: String)

Small helper to get SHALLOW copy of obj where prop ELIMINATED

const rmProp = (obj, prop) => ( (({[prop]:_, ...rest})=>rest)(obj) )

grpBy(src: Object[], key: String) : Object

Group Array by key. Root keys of a resulting array is value of specified key.

type name description
{Array} src The source array
{String} key The by key to group by
{Object} return Object with grouped objects as values
const grpBy = (src, key) => src.reduce((a, e) => (
  (a[e[key]] = a[e[key]] || []).push(rmProp(e, key)),  a
), {});

blowObj(obj : Object)

Collapse array of object if it consists of only object with single value. Replace it by the rest value.

const blowObj = obj => Array.isArray(obj) && obj.length === 1 && Object.values(obj[0]).length === 1 ? Object.values(obj[0])[0] : obj;

grpByReal(src: Object, keylist: string | string[])

Recursive grouping with list of keys. keyList may be an array of key names or comma separated list of key names whom UNIQUE values will becomes the keys of the resulting object.

const grpByReal = function (src, keyList) {
  const [key, ...rest] = Array.isArray(keyList) ? keyList : String(keyList).trim().split(/\s*,\s*/);
  const res = key ? grpBy(src, key) : [...src];
  if (rest.length) {
for (const k in res) {
  res[k] = grpByReal(res[k], rest)
}
  } else {
for (const k in res) {
  res[k] = blowObj(res[k])
}
  }
  return res;
}

DEMO

/******************** DEMO ***********************************************/
const data = [
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
  ];

console.log( JSON.stringify( grpByReal (data, 'Phase, Step, Task'), null, 2 ) );
/**
* Small helper to get SHALLOW copy of `obj` where `prop` ELIMINATED
*/
const rmProp = (obj, prop) => ( (({[prop]:_, ...rest})=>rest)(obj) )
/**
* Group Array by key. Root keys of a resulting array is value
* of specified key.
*
* @param {Array} src The source array
* @param {String} key The by key to group by
* @return {Object} Object with grouped objects as values
*/
const grpBy = (src, key) => src.reduce((a, e) => (
(a[e[key]] = a[e[key]] || []).push(rmProp(e, key)), a
), {});
/**
* Collapse array of object if it consists of only object with single value.
* Replace it by the rest value.
*/
const blowObj = obj => Array.isArray(obj) && obj.length === 1 && Object.values(obj[0]).length === 1 ? Object.values(obj[0])[0] : obj;
/**
* Recursive grouping with list of keys. `keyList` may be an array
* of key names or comma separated list of key names whom UNIQUE values will
* becomes the keys of the resulting object.
*/
const grpByReal = function (src, keyList) {
const [key, ...rest] = Array.isArray(keyList) ? keyList : String(keyList).trim().split(/\s*,\s*/);
const res = key ? grpBy(src, key) : [...src];
if (rest.length) {
for (const k in res) {
res[k] = grpByReal(res[k], rest)
}
} else {
for (const k in res) {
res[k] = blowObj(res[k])
}
}
return res;
}
/**
******************** DEMO ***********************************************
* @example
const inputArray = [
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];
console.log( JSON.stringify( grpByReal(inputArray, 'Phase, Step, Task'), null, 2 ) );
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment