[Functional Programming] 3장: group 함수와 Fx 클래스 분리

2025. 4. 23. 08:00프로그래밍 공부/javascript & typescript

반응형
SMALL

이번 장에서는 함수형 유틸리티의 대표적인 고급 함수 중 하나인 group 함수를 구현하고,
기존에 하나로 묶여 있던 Fx 클래스와 각 함수들을 모듈 단위로 분리하는 리팩토링을 진행했습니다.

1. group 함수란?

group은 주어진 키 생성 함수에 따라 데이터를 그룹핑하여 Map 형태로 반환하는 함수입니다.
기본적으로 다음과 같은 역할을 수행합니다.

group([{ age: 20 }, { age: 30 }, { age: 20 }], x => x.age)
// Map {
//   20 => [ { age: 20 }, { age: 20 } ],
//   30 => [ { age: 30 } ]
// }

함수형 프로그래밍에서는 reduce를 사용해서 구현할 수도 있지만,
이번에는 이터러블 기반 구현 + 타입 안정성을 고려해 group을 순수 유틸리티로 정의합니다.

2. group 함수 구현

// src/array/group.ts
export function group<T, K>(iter: Iterable<T>, keyFn: (item: T) => K): Map<K, T[]> {
  const map = new Map<K, T[]>();

  for (const item of iter) {
    const key = keyFn(item);
    if (!map.has(key)) map.set(key, []);
    map.get(key)!.push(item);
  }

  return map;
}
  • 입력값은 Iterable<T>이기 때문에 Array, Set, Generator 모두 지원
  • 반환값은 Map<K, T[]>로 키-그룹 형태
  • 타입스크립트의 제네릭을 활용해 keyFn에서 반환되는 타입 K가 정확히 추론됨

test code

import { group } from "./group";

describe("group", () => {
  it("groups numbers by even or odd", () => {
    const data = [1, 2, 3, 4, 5];
    const result = group(data, x => (x % 2 === 0 ? "even" : "odd"));
    expect(result.get("even")).toEqual([2, 4]);
    expect(result.get("odd")).toEqual([1, 3, 5]);
  });

  it("returns empty map for empty input", () => {
    const result = group([], x => x);
    expect(result.size).toBe(0);
  });
});

3. Fx 클래스와 유틸 함수 분리

├── Fx.ts
├── array/
│   ├── map.ts
│   ├── filter.ts
│   ├── index.ts
│   ├── take.ts
│   └── toArray.ts
// Fx.ts (일부)
import { map } from './array';

export class Fx<T> implements Iterable<T> {
  private cloneWith<U>(iter: Iterable<U>): Fx<U> {
    return new Fx(iter);
  }
  // ...
  map<U>(fn: (item: T) => U): Fx<U> {
    return this.cloneWith(map(this.iterable, fn));
  }
  // ...

각 연산을 외부에서 import → 내부 구현은 단순히 위임만 수행

반응형
LIST