Repository: exadel-inc/esl
Pull request #510 (2021-10-02 07:28:24)

feat: groupBy utility
Added a groupBy utility for esl-utils/misc module. Function transforms an array to the pairs object with a criteria value as a key and an array of original items that belongs to the current criteria value.

github-actions[bot]: **CLA Assistant Lite bot:** Thank you for your submission, we really appreciate it. We ask that you sign our  <a href="https://github.com/exadel-inc/esl/blob/main/CLA.md">Contributor Licence Agreement</a> before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format. 
   - - -
   ***I have read the CLA Document and I hereby sign the CLA***
   - - - 
   **2** out of **3** committers have signed the CLA.<br/>:white_check_mark: @abarmina<br/>:white_check_mark: @Anna-MariiaPetryk<br/>:x: @fshovchko<br/><sub>You can retrigger this bot by commenting **recheckcla** in this Pull Request</sub>
fshovchko: I have read the CLA Document and I hereby sign the CLA
fshovchko: recheckcla

Patch:
diff --git a/src/modules/esl-utils/misc/array.ts b/src/modules/esl-utils/misc/array.ts
index a727e0dea..5a9e48031 100644
--- a/src/modules/esl-utils/misc/array.ts
+++ b/src/modules/esl-utils/misc/array.ts
@@ -39,3 +39,14 @@ export function range(n: number, filler: (i: number) => any = identity): any[] {
   while (i < n) arr[i] = filler(i++);
   return arr;
 }
+
+/**
+ * @returns object with a criteria value as a key and an array of original items that belongs to the current criteria value
+ */
+export const groupBy = <T, V extends string | number>(array: T[], group: (item: T) => V): Record<V, T[]> => {
+  return array.reduce((obj: Record<V, T[]>, el: T) => {
+    const key = group(el);
+    obj[key] ? obj[key].push(el) : obj[key] = [el];
+    return obj;
+  }, {} as Record<V, T[]>);
+};


Test patch:
diff --git a/src/modules/esl-utils/misc/test/array.test.ts b/src/modules/esl-utils/misc/test/array.test.ts
index ed933e56a..aaeecdc99 100644
--- a/src/modules/esl-utils/misc/test/array.test.ts
+++ b/src/modules/esl-utils/misc/test/array.test.ts
@@ -1,4 +1,4 @@
-import {flat, range, tuple, uniq, wrap} from '../array';
+import {flat, range, tuple, uniq, wrap, groupBy} from '../array';
 
 describe('misc/array helper tests', () => {
   test('tuple', () => {
@@ -45,3 +45,19 @@ describe('misc/array helper tests', () => {
     expect(range(9, (x) => x / 8)).toEqual([...Array(9).keys()].map((x) => x / 8))
   });
 });
+
+type V = string | number;
+
+describe('misc/groupBy', () => {
+  test.each([
+    [[1, 2, 10, 4, 3], (i: number) => i % 2 > 0 ? 'odd' : 'even', { odd: [1, 3], even: [2, 10, 4] }],
+    [['cat', 'fat', 'kitten'], (i: string) => i.length, { 3: ['cat', 'fat'], 6: ['kitten']}],
+    [['Fog', 'Tea', 'ocean', 'rain'], (i: string) => i[0] === i[0].toUpperCase() ? 'uppercase' : 'lowercase', { uppercase: ['Fog', 'Tea'], lowercase: ['ocean', 'rain']}],
+    [[{ a: 2, b: 1 }, {b: 2}, {a: 1}, {}], (i: Record<string, number>) => i['a'] ? i['a'] : 'undefined', { 2: [{a: 2, b: 1}], undefined: [{b: 2}, {}], 1: [{a: 1}] }],
+    [[100, 20, 10, 101, 300], (i: number) => String(i)[0], { '1': [100, 10, 101], '2': [20], '3': [300] }],
+    [[], (i: any) => i, {}],
+    [[[5, 2], [1, 0], [5, 4]], (i: number[]) => i[0], { 5: [[5, 2], [5, 4]], 1: [[1,0]]}]
+  ])('array %p with criteria %p to transform into %o', (a: any[], b: (t: any) => V, expected: Record<V, any>) => {
+    expect(groupBy(a, b)).toEqual(expected);
+  });
+});