![見出し画像](https://assets.st-note.com/production/uploads/images/119358998/rectangle_large_type_2_f564b4ce125a62691b1102a81e236880.png?width=1200)
連勝・連敗のストリーク検出を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.
簡単ですが、以上です。
この記事が気に入ったらサポートをしてみませんか?