← 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]);
});
System prompt
You are an expert programmer. Solve the task exactly as specified. Output your solution as fenced code blocks using the required file name(s) and the exact function/type signatures requested. Prefer correctness; do not include prose outside code unless asked.
Proposed solution
```ts
export function groupBy<T, K extends string | number>(
  items: T[],
  keyFn: (item: T) => K,
): Record<K, T[]>
```

Test output (stdout)

TAP version 13
# /tmp/llmlab-ts-02-groupby-k9ln1gxv/solution.test.ts:3
# import { groupBy } from "./solution.ts";
#          ^
# SyntaxError: The requested module './solution.ts' does not provide an export named 'groupBy'
#     at \#asyncInstantiate (node:internal/modules/esm/module_job:327:21)
#     at async ModuleJob.run (node:internal/modules/esm/module_job:431:5)
#     at async node:internal/modules/esm/loader:633:26
#     at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)
# Node.js v24.16.0
# Subtest: solution.test.ts
not ok 1 - solution.test.ts
  ---
  duration_ms: 66.202031
  type: 'test'
  location: '/tmp/llmlab-ts-02-groupby-k9ln1gxv/solution.test.ts:1:1'
  failureType: 'testCodeFailure'
  exitCode: 1
  signal: ~
  error: 'test failed'
  code: 'ERR_TEST_FAILURE'
  ...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 70.734707
Self-repair attempts
[
  {
    "total": 1,
    "answer": "```ts\nexport function groupBy<T, K extends string | number>(\n  items: T[],\n  keyFn: (item: T) => K,\n): Record<K, T[]>\n```",
    "passed": 0,
    "test_error": "TAP version 13\n# /tmp/llmlab-ts-02-groupby-k9ln1gxv/solution.test.ts:3\n# import { groupBy } from \"./solution.ts\";\n#          ^\n# SyntaxError: The requested module './solution.ts' does not provide an export named 'groupBy'\n#     at \\#asyncInstantiate (node:internal/modules/esm/module_job:327:21)\n#     at async ModuleJob.run (node:internal/modules/esm/module_job:431:5)\n#     at async node:internal/modules/esm/loader:633:26\n#     at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)\n# Node.js v24.16.0\n# Subtest: solution.test.ts\nnot ok 1 - solution.test.ts\n  ---\n  duration_ms: 66.202031\n  type: 'test'\n  location: '/tmp/llmlab-ts-02-groupby-k9ln1gxv/solution.test.ts:1:1'\n  failureType: 'testCodeFailure'\n  exitCode: 1\n  signal: ~\n  error: 'test failed'\n  code: 'ERR_TEST_FAILURE'\n  ...\n1..1\n# tests 1\n# suites 0\n# pass 0\n# fail 1\n# cancelled 0\n# skipped 0\n# todo 0\n# duration_ms 70.734707"
  },
  {
    "total": 6,
    "answer": "```ts\nexport function groupBy<T, K extends string | number>(\n  items: T[],\n  keyFn: (item: T) => K,\n): Record<K, T[]> {\n  return items.reduce((acc, item) => {\n    const key = keyFn(item);\n    if (!acc[key]) {\n      acc[key] = [];\n    }\n    acc[key].push(item);\n    return acc;\n  }, {} as Record<K, T[]>);\n}\n```",
    "passed": 6,
    "test_error": ""
  }
]