見出し画像

連勝・連敗のストリーク検出をTypescriptで実現するやり方

こんにちわ。nap5です。


連勝・連敗のストリーク検出をTypescriptで実現するやり方の紹介です。


前回までの記事と関連です。基本はpartitionByで前回と今回をreduceのコンテキストで判定しながらグルーピングしていく感じになります。






定義側になります。

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;
  }, []);


使用側になります。

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";

describe("条件に基づいた連続値のグループ化", () => {
  type GameResult = {
    date: string;
    result: 0 | 1 | 2; // 0: 負け, 1: 勝ち, 2: 引き分け
  };

  const inputData: GameResult[] = [
    { date: "2023-10-01", result: 1 },
    { date: "2023-10-07", result: 1 },
    { date: "2023-10-08", result: 1 },
    { date: "2023-10-09", result: 2 },
    { date: "2023-10-10", result: 1 },
    { date: "2023-10-14", result: 0 },
    { date: "2023-10-15", result: 0 },
    { date: "2023-10-20", result: 2 },
    { date: "2023-10-21", result: 0 },
    { date: "2023-10-22", result: 0 },
    { date: "2023-10-23", result: 1 },
    { date: "2023-10-28", result: 1 },
    { date: "2023-10-29", result: 1 },
    { date: "2023-11-03", result: 0 },
    { date: "2023-11-04", result: 1 },
    { date: "2023-11-05", result: 1 },
    { date: "2023-11-11", result: 1 },
    { date: "2023-11-12", result: 0 },
    { date: "2023-11-18", result: 0 },
    { date: "2023-11-19", result: 0 },
    { date: "2023-11-24", result: 1 },
    { date: "2023-11-25", result: 0 },
    { date: "2023-11-26", result: 0 },
  ];

  test("3連勝以上した日をグループ化", () => {
    const outputData = pipe(
      inputData,
      bindTo("input"),
      bind("partitioned", ({ input }) => partitionBy(input, ["result"])),
      bind("output", ({ partitioned }) =>
        partitioned.filter(
          (g) => g.length >= 3 && g.every((d) => d.result === 1)
        )
      ),
      map(({ output }) => output)
    );
    expect(outputData).toStrictEqual([
      [
        { date: "2023-10-01", result: 1 },
        { date: "2023-10-07", result: 1 },
        { date: "2023-10-08", result: 1 },
      ],
      [
        { date: "2023-10-23", result: 1 },
        { date: "2023-10-28", result: 1 },
        { date: "2023-10-29", result: 1 },
      ],
      [
        { date: "2023-11-04", result: 1 },
        { date: "2023-11-05", result: 1 },
        { date: "2023-11-11", result: 1 },
      ],
    ]);
  });

  test("3連敗以上した日をグループ化", () => {
    const outputData = pipe(
      inputData,
      bindTo("input"),
      bind("partitioned", ({ input }) => partitionBy(input, ["result"])),
      bind("output", ({ partitioned }) =>
        partitioned.filter(
          (g) => g.length >= 3 && g.every((d) => d.result === 0)
        )
      ),
      map(({ output }) => output)
    );
    expect(outputData).toStrictEqual([
      [
        { date: "2023-11-12", result: 0 },
        { date: "2023-11-18", result: 0 },
        { date: "2023-11-19", result: 0 },
      ],
    ]);
  });

  test("2連敗以上した日をグループ化", () => {
    const outputData = pipe(
      inputData,
      bindTo("input"),
      bind("partitioned", ({ input }) => partitionBy(input, ["result"])),
      bind("output", ({ partitioned }) =>
        partitioned.filter(
          (g) => g.length >= 2 && g.every((d) => d.result === 0)
        )
      ),
      map(({ output }) => output)
    );
    console.log(outputData);
    expect(outputData).toStrictEqual([
      [
        { date: "2023-10-14", result: 0 },
        { date: "2023-10-15", result: 0 },
      ],
      [
        { date: "2023-10-21", result: 0 },
        { date: "2023-10-22", result: 0 },
      ],
      [
        { date: "2023-11-12", result: 0 },
        { date: "2023-11-18", result: 0 },
        { date: "2023-11-19", result: 0 },
      ],
      [
        { date: "2023-11-25", result: 0 },
        { date: "2023-11-26", result: 0 },
      ],
    ]);
  });
});



demo code.


簡単ですが、以上です。


この記事が気に入ったらサポートをしてみませんか?