Skip to content

Instantly share code, notes, and snippets.

@AaronHarris
Last active June 3, 2020 08:06
Show Gist options
  • Save AaronHarris/ceba94c647b4d3e71886c147bbf2d9ab to your computer and use it in GitHub Desktop.
Save AaronHarris/ceba94c647b4d3e71886c147bbf2d9ab to your computer and use it in GitHub Desktop.
Fun with lodash-fp's flow
/**
* Nested grouping of a list of objects by multiple nested keys
* Unwraps groupByAll(['dataset_name', 'table_name', 'column_name']) to something like:
*
* ```
* _.flow(
* _.groupBy('dataset_name'),
* _.mapValues(_.groupBy('table_name')),
* _.mapValues(_.mapValues(_.groupBy('column_name')))
* )
* ```
* or its equivalent
*
* ```
* _.flow(
* _.groupBy('dataset_name'),
* _.flow(_.groupBy, _.mapValues)('table_name')
* _.flow(_.groupBy, _.mapalues, _.mapValues)('column_name')
* )
* ```
* Source: [GitHub](https://github.com/lodash/lodash/issues/3894#issuecomment-409689045)
*/
const groupByAll = (iteratees: any) =>
fp.flow(
(fp.map as any).convert({cap:false})(
(iteratee: any, i: any) =>
fp.flow(fp.groupBy, ...fp.times(() => fp.mapValues, i))(iteratee),
iteratees
)
);
/**
* @todo This is untested
* Unwraps groupByAllThen(['dataset_name', 'table_name', 'column_name'], _.map('old_column_name')) to something like:
*
* @example
* ```
* _.flow(
* _.groupBy('dataset_name'),
* _.mapValues(_.groupBy('table_name')),
* _.mapValues(_.mapValues(_.groupBy('column_name'))),
* _.mapValues(_.mapValues(_.map('old_column_name')))
* )
* ```
*
* @example
* groupByAllThen(
* ['dataset_name', 'table_name', 'column_name'],
* fp.flow(
* fp.map('old_column_name'),
* fp.flatMap(fp.split(',')),
* fp.map(fp.trim)
* )
* ),
* fp.pickBy(sizeGt1),
* fp.pickBy(fp.negate(fp.isEmpty))
* )
*
* becomes
*
* ```
* _.flow(
* _.groupBy('dataset_name'),
* _.mapValues(_.groupBy('table_name')),
* _.mapValues(_.mapValues(_.groupBy('column_name'))),
* _.mapValues(_.mapValues(_.mapValues(_.flow( // GroupAndMapAndSplit
* fp.map('old_column_name'),
* fp.flatMap(fp.split(',')),
* fp.map(fp.trim)
* )))),
* _.mapValues(_.mapValues(fp.pickBy(sizeGt1))), // KeepOnlyMultiple
* _.mapValues(_.pickBy(_.negate(_.isEmpty)))
*
*/
const groupByAllThen = (iteratees: any, ...ops: any) =>
fp.flow(
(fp.map as any).convert({cap:false})(
(iteratee: any, i: any) =>
fp.flow(fp.groupBy, ...fp.times(() => fp.mapValues, i))(iteratee),
iteratees
),
(fp.map as any).convert({cap:false})(
(op: any, i: any) =>
fp.flow(op, ...fp.times(() => fp.mapValues, iteratees.length-i)),
ops
),
);
const isObjOrArr = (obj: any) => fp.isPlainObject(obj) || fp.isArray(obj);
/**
* Like compact for arrays, but removes keys that point to empty values
* @param object
* @returns a
*/
const compactObj = fp.pickBy(fp.cond([
[isObjOrArr, fp.negate(fp.isEmpty)],
[fp.isNil, fp.stubFalse],
[fp.stubTrue, fp.stubTrue]
]));
// compactObjDeep has some kinks due to isEmpty returning false for numbers and booleans
const compactObjDeep = (obj: any): any => {
if (fp.isArray(obj)) {
if (fp.isEmpty(obj)) { return undefined; }
return fp.compact(fp.map(compactObjDeep, obj));
}
if (fp.isPlainObject(obj)) {
if (fp.isEmpty(obj)) { return undefined; }
return compactObj(fp.mapValues(compactObjDeep, obj as any));
}
return obj;
};
/**
* @note This works with the non-fp lodash, but I feel is prone to bugs
* @param array of objects
* @return a unique list of the mapped elements
* @example _.mapUniq(array, 'column_name')
const mapUniq = _.flow(_.map, _.uniqBy);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment