見出し画像

CsharpのLINQで紹介されている「連続するキーで結果をグループ化する」をTypescriptで実現するやり方

こんにちわ。nap5です。


CsharpのLINQで紹介されている「連続するキーで結果をグループ化する」をTypescriptで実現するやり方の紹介です。


元ネタはこちらになります。


前回の記事で紹介したpartitionByを使って実現できます。


import { Path } from "dot-path-value";

export type TRow = Record<string, unknown>;

export const compare = <T extends TRow>(fields: Path<T>[], a: T, b: T): number => {
  for (const field of fields) {
    if (a[field] < b[field]) return -1;
    if (a[field] > b[field]) return 1;
  }
  return 0;
};

export const partitionBy = <T extends TRow>(data: T[], fields: Path<T>[]): T[][] =>
  data.reduce((acc: T[][], row: T) => {
    if (
      acc.length > 0 &&
      compare(
        fields,
        acc[acc.length - 1][acc[acc.length - 1].length - 1],
        row
      ) === 0
    ) {
      // 現在の行と直前のパーティションの最後の行が指定されたフィールドすべてで同じ場合に
      // 直前のパーティションに現在の行を追加する
      acc[acc.length - 1].push(row);
    } else {
      // 新しいパーティションを作成し、現在の行を追加する
      acc.push([row]);
    }
    return acc;
  }, []);


このpartitionByを使うと以下のように実装できます。

import { describe, test, expect } from "vitest";
import { bind, bindTo, map } from "fp-ts/lib/Identity";
import { pipe } from "fp-ts/lib/function";
import { partitionBy } from "./utils/dataUtil";

// @see https://learn.microsoft.com/ja-jp/dotnet/csharp/linq/group-results-by-contiguous-keys
describe("連続するキーで結果をグループ化する", () => {
  type Item = {
    key: string;
    value: string;
  };

  const inputData: Item[] = [
    { key: "A", value: "We" },
    { key: "A", value: "think" },
    { key: "A", value: "that" },
    { key: "B", value: "LINQ" },
    { key: "C", value: "is" },
    { key: "A", value: "really" },
    { key: "B", value: "cool" },
    { key: "B", value: "!" },
  ];

  test("Use -> partitionBy", () => {
    const outputData = pipe(
      inputData,
      bindTo("input"),
      bind("partitioned", ({ input }) => partitionBy(input, ["key"])),
      bind("output", ({ partitioned }) =>
        partitioned.map((g) => g.map((d) => d.value))
      ),
      map(({ output }) => output)
    );
    expect(outputData).toStrictEqual([
      ["We", "think", "that"],
      ["LINQ"],
      ["is"],
      ["really"],
      ["cool", "!"],
    ]);
  });
});


demo code.


簡単ですが、以上です。


いいなと思ったら応援しよう!