← run

ts-02-groupby

0.000
0/1 tests· typing
Challenge · difficulty 2/5
# Generic `groupBy`

Implement **`solution.ts`** exporting a generic function:

```ts
export function groupBy<T, K extends string | number>(
  items: T[],
  keyFn: (item: T) => K,
): Record<K, T[]>
```

Group the `items` into a record keyed by the result of `keyFn(item)`. Each value is
the array of items that produced that key, **in their original order**.

- `groupBy([1, 2, 3, 4], (n) => (n % 2 === 0 ? "even" : "odd"))`
  → `{ odd: [1, 3], even: [2, 4] }`
- `groupBy([], (n: number) => n)` → `{}`
- A numeric key works too:
  `groupBy(["a", "bb", "ccc", "dd"], (s) => s.length)`
  → `{ 1: ["a"], 2: ["bb", "dd"], 3: ["ccc"] }`

Keep it fully typed (must pass `tsc --noEmit` in strict mode). Do not use `any` in the
public API.
tests/solution.test.ts
import { test } from "node:test";
import { strict as assert } from "node:assert";
import { groupBy } from "./solution.ts";

test("groups by string key, preserving order", () => {
  const r = groupBy([1, 2, 3, 4, 5], (n) => (n % 2 === 0 ? "even" : "odd"));
  assert.deepEqual(r, { odd: [1, 3, 5], even: [2, 4] });
});

test("empty input yields empty record", () => {
  assert.deepEqual(groupBy<number, number>([], (n) => n), {});
});

test("groups by numeric key", () => {
  const r = groupBy(["a", "bb", "ccc", "dd"], (s) => s.length);
  assert.deepEqual(r, { 1: ["a"], 2: ["bb", "dd"], 3: ["ccc"] });
});

test("groups objects by a property", () => {
  const people = [
    { name: "Ana", city: "NYC" },
    { name: "Bo", city: "LA" },
    { name: "Cy", city: "NYC" },
  ];
  const r = groupBy(people, (p) => p.city);
  assert.deepEqual(r, {
    NYC: [
      { name: "Ana", city: "NYC" },
      { name: "Cy", city: "NYC" },
    ],
    LA: [{ name: "Bo", city: "LA" }],
  });
});

test("single group when keyFn is constant", () => {
  const r = groupBy([1, 2, 3], () => "all");
  assert.deepEqual(r, { all: [1, 2, 3] });
});

test("does not mutate the input array", () => {
  const input = [3, 1, 2];
  groupBy(input, (n) => n);
  assert.deepEqual(input, [3, 1, 2]);
});
Proposed solution
```typescript
export function groupBy(arr: Item, (item: Item => Item) => {
  1: [ "a", "3", ] = { },
  2: [ "a", "bb", "cc", "dd" ] = { },
  3: [ "a", "bb", "cc", "dd" ] = { },
  4: [ "a", "3", "cc", "dd" ] = { },
  5: [ "a", "3", "cc", "dd" ] = { },
  6: [ "a", "3", "cc", "dd" ] = { },
  7: [ "a", "3", "cc", "dd" ] = { },
  8: [ "a", "3", "cc", "dd" ] = { },
  9: [ "a", "3", "cc", "dd" ] = { },
  10: [ "a", "3", "cc", "dd" ] = { },
  11: [ "a", "3", "cc", "dd" ] = { },
  12: [ "a", "3", "cc", "dd" ] = { },
  13: [ "a", "3", "cc", "dd" ] = { },
  14: [ "a", "3", "cc", "dd" ] = { },
  15: [ "a", "3", "cc", "dd" ] = { },
  16: [ "a", "3", "cc", "dd" ] = { },
  17: [ "a", "3", "cc", "dd" ] = { },
  18: [ "a", "3", "cc", "dd" ] = { },
  19: [ "a", "3", "cc", "dd" ] = { },
  20: [ "a", "3", "cc", "dd" ] = { },
  21: [ "a", "3", "cc", "dd" ] = { },
  22: [ "a", "3", "cc", "dd" ] = { },
  23: [ "a", "3", "cc", "dd" ] = { },
  24: [ "a", "3", "cc", "dd" ] = { },
  25: [ "a", "3", "cc", "dd" ] = { },
  26: [ "a", "3", "cc", "dd" ] = { },
  27: [ "a", "3", "cc", "dd" ] = { },
  28: [ "a", "3", "cc", "dd" ] = { },
  29: [ "a", "3", "cc", "dd" ] = { },
  30: [ "a", "3", "cc", "dd" ] = { },
  31: [ "a", "3", "cc", "dd" ] = { },
  32: [ "a", "3", "cc", "dd" ] = { },
  33: [ "a", "3", "cc", "dd" ] = { },
  34: [ "a", "3", "cc", "dd" ] = { },
  35: [ "a", "3", "cc", "dd" ] = { },
  36: [ "a", "3", "cc", "dd" ] = { },
  37: [ "a", "3", "cc", "dd" ] = { },
  38: [ "a", "3", "cc", "dd" ] = { },
  39: [ "a", "3", "cc", "dd" ] = { },
  40: [ "a", "3", "cc", "dd" ] = { },
  41: [ "a", "3", "cc", "dd" ] = { },
  42: [ "a", "3", "cc", "dd" ] = { },
  43: [ "a", "3", "cc", "dd" ] = { },
  44: [ "a", "3", "cc", "dd" ] = { },
  45: [ "a", "3", "cc", "dd" ] = { },
  46: [ "a", "3", "cc", "dd" ] = { },
  47: [ "a", "3", "cc", "dd" ] = { },
  48: [ "a", "3", "cc", "dd" ] = { },
  49: [ "a", "3", "cc", "dd" ] = { },
  50: [ "a", "3", "cc", "dd" ] = { },
  51: [ "a", "3", "cc", "dd" ] = { },
  52: [ "a", "3", "cc", "dd" ] = { },
  53: [ "a", "3", "cc", "dd" ] = { },
  54: [ "a", "3", "cc", "dd" ] = { },
  55: [ "a", "3", "cc", "dd" ] = { },
  56: [ "a", "3", "cc", "dd" ] = { },
  57: [ "a", "3", "cc", "dd" ] = { },
  58: [ "a", "3", "cc", "dd" ] = { },
  59: [ "a", "3", "cc", "dd" ] = { },
  60: [ "a", "3", "cc", "dd" ] = { },
  61: [ "a", "3", "cc", "dd" ] = { },
  62: [ "a", "3", "cc", "dd" ] = { },
  63: [ "a", "3", "cc", "dd" ] = { },
  64: [ "a", "3", "cc", "dd" ] = { },
  65: [ "a", "3", "cc", "dd" ] = { },
  66: [ "a", "3", "cc", "dd" ] = { },
  67: [ "a", "3", "cc", "dd" ] = { },
  68: [ "a", "3", "cc", "dd" ] = { },
  69: [ "a", "3", "cc", "dd" ] = { },
  70: [ "a", "3", "cc", "dd" ] = { },
  71: [ "a", "3", "cc", "dd" ] = { },
  72: [ "a", "3", "cc", "dd" ] = { },
  73: [ "a", "3", "cc", "dd" ] = { },
  74: [ "a", "3", "cc", "dd" ] = { },
  75: [ "a", "3", "cc", "dd" ] = { },
  76: [ "a", "3", "cc", "dd" ] = { },
  77: [ "a", "3", "cc", "dd" ] = { },
  78: [ "a", "3", "cc", "dd" ] = { },
  79: [ "a", "3", "cc", "dd" ] = { },
  80: [ "a", "3", "cc", "dd" ] = { },
  81: [ "a", "3", "cc", "dd" ] = { },
  82: [ "a", "3", "cc", "dd" ] = { },
  83: [ "a", "3", "cc", "dd" ] = { },
  84: [ "a", "3", "cc", "dd" ] = { },
  85: [ "a", "3", "cc", "dd" ] = { },
  86: [ "a", "3", "cc", "dd" ] = { },
  87: [ "a", "3", "cc", "dd" ] = { },
  88: [ "a", "3", "cc", "dd" ] = { },
  89: [ "a", "3", "cc", "dd" ] = { },
  90: [ "a", "3", "cc", "dd" ] = { },
  91: [ "a", "3", "cc", "dd" ] = { },
  92: [ "a", "3", "cc", "dd" ] = { },
  93: [ "a", "3", "cc", "dd" ] = { },
  94: [ "a", "3", "cc", "dd" ] = { },
  95: [ "a", "3", "cc", "dd" ] = { },
  96: [ "a", "3", "cc", "dd" ] = { },
  97: [ "a", "3", "cc", "dd" ] = { },
  98: [ "a", "3", "cc", "dd" ] = { },
  99: [ "a", "3", "cc", "dd" ] = { },
  100: [ "a", "3", "cc", "dd" ] = { },
  101: [ "a", "3", "cc", "dd" ] = { },
  102: [ "a", "3", "cc", "dd" ] = { },
  103: [ "a", "3", "cc", "dd" ] = { },
  104: [ "a", "3", "cc", "dd" ] = { },
  105: [ "a", "3", "cc", "dd" ] = { },
  106: [ "a", "3", "cc", "dd" ] = { },
  107: [ "a", "3", "cc", "dd" ] = { },
  108: [ "a", "3", "cc", "dd" ] = { },
  109: [ "a", "3", "cc", "dd" ] = { },
  110: [ "a", "3", "cc", "dd" ] = { },
  111: [ "a", "3", "cc", "dd" ] = { },
  112: [ "a", "3", "cc", "dd" ] = { },
  113: [ "a", "3", "cc", "dd" ] = { },
  114: [ "a", "3", "cc", "dd" ] = { },
  115: [ "a", "3", "cc", "dd" ] = { },
  116: [ "a", "3", "cc", "dd" ] = { },
  117: [ "a", "3", "cc", "dd" ] = { },
  118: [ "a", "3", "cc", "dd" ] = { },
  119: [ "a", "3", "cc", "dd" ] = { },
  120: [ "a", "3", "cc", "dd" ] = { },
  121: [ "a", "3", "cc", "dd" ] = { },
  122: [ "a", "3", "cc", "dd" ] = { },
  123: [ "a", "3", "cc", "dd" ] = { },
  124: [ "a", "3", "cc", "dd" ] = { },
  125: [ "a", "3", "cc", "dd" ] = { },
  126: [ "a", "3", "cc", "dd" ] = { },
  127: [ "a", "3", "cc", "dd" ] = { },
  128: [ "a", "3", "cc", "dd" ] = { },
  129: [ "a", "3", "cc", "dd" ] = { },
  130: [ "a", "3", "cc", "dd" ] = { },
  131: [ "a", "3", "cc", "dd" ] = { },
  132: [ "a", "3", "cc", "dd" ] = { },
  133: [ "a", "3", "cc", "dd" ] = { },
  134: [ "a", "3", "cc", "dd" ] = { },
  135: [ "a", "3", "cc", "dd" ] = { },
  136: [ "a", "3", "cc", "dd" ] = { },
  137: [ "a", "3", "cc", "dd" ] = { },
  138: [ "a", "3", "cc", "dd" ] = { },
  139: [ "a", "3", "cc", "dd" ] = { },
  140: [ "a", "3", "cc", "dd" ] = { },
  141: [ "a", "3", "cc", "dd" ] = { },
  142: [ "a", "3", "cc", "dd" ] = { },
  143: [ "a", "3", "cc", "dd" ] = { },
  144: [ "a", "3", "cc", "dd" ] = { },
  145: [ "a", "3", "cc", "dd" ] = { },
  146: [ "a", "3", "cc", "dd" ] = { },
  147: [ "a", "3", "cc", "dd" ] = { },
  148: [ "a", "3", "cc", "dd" ] = { },
  149: [ "a", "3", "cc", "dd" ] = { },
  150: [ "a", "3", "cc", "dd" ] = { },
  151: [ "a", "3", "cc", "dd" ] = { },
  152: [ "a", "3", "cc", "dd" ] = { },
  153: [ "a", "3", "cc", "dd" ] = { },
  154: [ "a", "3", "cc", "dd" ] = { },
  155: [ "a", "3", "cc", "dd" ] = { },
  156: [ "a", "3", "cc", "dd" ] = { },
  157: [ "a", "3", "cc", "dd" ] = { },
  158: [ "a", "3", "cc", "dd" ] = { },
  159: [ "a", "3", "cc", "dd" ] = { },
  160: [ "a", "3", "cc", "dd" ] = { },
  161: [ "a", "3", "cc", "dd" ] = { },
  162: [ "a", "3", "cc", "dd" ] = { },
  163: [ "a", "3", "cc", "dd" ] = { },
  164: [ "a", "3", "cc", "dd" ] = { },
  165: [ "a", "3", "cc", "dd" ] = { },
  166: [ "a", "3", "cc", "dd" ] = { },
  167: [ "a", "3", "cc", "dd" ] = { },
  168: [ "a", "3", "cc", "dd" ] = { },
  169: [ "a", "3", "cc", "dd" ] = { },
  170: [ "a", "3", "cc", "dd" ] = { },
  171: [ "a", "3", "cc", "dd" ] = { },
  172: [ "a", "3", "cc", "dd" ] = { },
  173: [ "a", "3", "cc", "dd" ] = { },
  174: [ "a", "3", "cc", "dd" ] = { },
  175: [ "a", "3", "cc", "dd" ] = { },
  176: [ "a", "3", "cc", "dd" ] = { },
  177: [ "a", "3", "cc", "dd" ] = { },
  178: [ "a", "3", "cc", "dd" ] = { },
  179: [ "a", "3", "cc", "dd" ] = { },
  180: [ "a", "3", "cc", "dd" ] = { },
  181: [ "a", "3", "cc", "dd" ] = { },
  182: [ "a", "3", "cc", "dd" ] = { },
  183: [ "a", "3", "cc", "dd" ] = { },
  184: [ "a", "3", "cc", "dd" ] = { },
  185: [ "a", "3", "cc", "dd" ] = { },
  186: [ "a", "3", "cc", "dd" ] = { },
  187: [ "a", "3", "cc", "dd" ] = { },
  188: [ "a", "3", "cc", "dd" ] = { },
  189: [ "a", "3", "cc", "dd" ] = { },
  190: [ "a", "3", "cc", "dd" ] = { },
  191: [ "a", "3", "cc", "dd" ] = { },
  192: [ "a", "3", "cc", "dd" ] = { },
  193: [ "a", "3", "cc", "dd" ] = { },
  194: [ "a", "3", "cc", "dd" ] = { },
  195: [ "a", "3", "cc", "dd" ] = { },
  196: [ "a", "3", "cc", "dd" ] = { },
  197: [ "a", "3", "cc", "dd" ] = { },
  198: [ "a", "3", "cc", "dd" ] = { },
  199: [ "a", "3", "cc", "dd" ] = { },
  200: [ "a", "3", "cc", "dd" ] = { },
  201: [ "a", "3", "cc", "dd" ] = { },
  202: [ "a", "3", "cc", "dd" ] = { },
  203: [ "a", "3", "cc", "dd" ] = { },
  204: [ "a", "3", "cc", "dd" ] = { },
  205: [ "a", "3", "cc", "dd" ] = { },
  206: [ "a", "3", "cc", "dd" ] = { },
  207: [ "a", "3", "cc", "dd" ] = { },
  208: [ "a", "3", "cc", "dd" ] = { },
  209: [ "a", "3", "cc", "dd" ] = { },
  210: [ "a", "3", "cc", "dd" ] = { },
  211: [ "a", "3", "cc", "dd" ] = { },
  212: [ "a", "3", "cc", "dd" ] = { },
  213: [ "a", "3", "cc", "dd" ] = { },
  214: [ "a", "3", "cc", "dd" ] = { },
  215: [ "a", "3", "cc", "dd" ] = { },
  216: [ "a", "3", "cc", "dd" ] = { },
  217: [ "a", "3", "cc", "dd" ] = { },
  218: [ "a", "3", "cc", "dd" ] = { },
  219: [ "a", "3", "cc", "dd" ] = { },
  220: [ "a", "3", "cc", "dd" ] = { },
  221: [ "a", "3", "cc", "dd" ] = { },
  222: [ "a", "3", "cc", "dd" ] = { },
  223: [ "a", "3", "cc", "dd" ] = { },
  224: [ "a", "3", "cc", "dd" ] = { },
  225: [ "a", "3", "cc", "dd" ] = { },
  226: [ "a", "3", "cc", "dd" ] = { },
  227: [ "a", "3", "cc", "dd" ] = { },
  228: [ "a", "3", "cc", "dd" ] = { },
  229: [ "a", "3", "cc", "dd" ] = { },
  230: [ "a", "3", "cc", "dd" ] = { },
  231: [ "a", "3", "cc", "dd" ] = { },
  232: [ "a", "3", "cc", "dd" ] = { },
  233: [ "a", "3", "cc", "dd" ] = { },
  234: [ "a", "3", "cc", "dd" ] = { },
  235: [ "a", "3", "cc", "dd" ] = { },
  236: [ "a", "3", "cc", "dd" ] = { },
  237: [ "a", "3", "cc", "dd" ] = { },
  238: [ "a", "3", "cc", "dd" ] = { },
  239: [ "a", "3", "cc", "dd" ] = { },
  240: [ "a", "3", "cc", "dd" ] = { },
  241: [ "a", "3", "cc", "dd" ] = { },
  242: [ "a", "3", "cc", "dd" ] = { },
  243: [ "a", "3", "cc", "dd" ] = { },
  244: [ "a", "3", "cc", "dd" ] = { },
  245: [ "a", "3", "cc", "dd" ] = { },
  246: [ "a", "3", "cc", "dd" ] = { },
  247: [ "a", "3", "cc", "dd" ] = { },
  248: [ "a", "3", "cc", "dd" ] = { },
  249: [ "a", "3", "cc", "dd" ] = { },
  250: [ "a", "3", "cc", "dd" ] = { },
  251: [ "a", "3", "cc", "dd" ] = { },
  252: [ "a", "3", "cc", "dd" ] = { },
  253: [ "a", "3", "cc", "dd" ] = { },
  254: [ "a", "3", "cc", "dd" ] = { },
  255: [ "a", "3", "cc", "dd" ] = { },
  256: [ "a", "3", "cc", "dd" ] = { },
  257: [ "a", "3", "cc", "dd" ] = { },
  258: [ "a", "3", "cc", "dd" ] = { },
  259: [ "a", "3", "cc", "dd" ] = { },
  260: [ "a", "3", "cc", "dd" ] = { },
  261: [ "a", "3", "cc", "dd" ] = { },
  262: [ "a", "3", "cc", "dd" ] = { },
  263: [ "a", "3", "cc", "dd" ] = { },
  264: [ "a", "3", "cc", "dd" ] = { },
  265: [ "a", "3", "cc", "dd" ] = { },
  266: [ "a", "3", "cc", "dd" ] = { },
  267: [ "a", "3", "cc", "dd" ] = { },
  268: [ "a", "3", "cc", "dd" ] = { },
  269: [ "a", "3", "cc", "dd" ] = { },
  270: [ "a", "3", "cc", "dd" ] = { },
  271: [ "a", "3", "cc", "dd" ] = { },
  272: [ "a", "3", "cc", "dd" ] = { },
  273: [ "a", "3", "cc", "dd" ] = { },
  274: [ "a", "3", "cc", "dd" ] = { },
  275: [ "a", "3", "cc", "dd" ] = { },
  276: [ "a", "3", "cc", "dd" ] = { },
  277: [ "a", "3", "cc", "dd" ] = { },
  278: [ "a", "3", "cc", "dd" ] = { },
  279: [ "a", "3", "cc", "dd" ] = { },
  280: [ "a", "3", "cc", "dd" ] = { },
  281: [ "a", "3", "cc", "dd" ] = { },
  282: [ "a", "3", "cc", "dd" ] = { },
  283: [ "a", "3", "cc", "dd" ] = { },
  284: [ "a", "3", "cc", "dd" ] = { },
  285: [ "a", "3", "cc", "dd" ] = { },
  286: [ "a", "3", "cc", "dd" ] = { },
  287: [ "a", "3", "cc", "dd" ] = { },
  288: [ "a", "3", "cc", "dd" ] = { },
  289: [ "a", "3", "cc", "dd" ] = { },
  290: [ "a", "3", "cc", "dd" ] = { },
  291: [ "a", "3", "cc", "dd" ] = { },
  292: [ "a", "3", "cc", "dd" ] =

Errors (stderr)

no code extracted from response