LoginSignup
2
0

More than 3 years have passed since last update.

【TypeScript】配列から指定個数だけ無作為に非復元抽出する関数

Posted at

コード

// 関数インターフェイスのための型関数定義
type Append<T, U extends unknown[]> = ((x: T, ...y: U) => void) extends (...x: infer V) => void
    ? V
    : never;

declare type FixedLengthTuple<T, L extends number, R extends any[] = []> = {
    0: R;
    1: FixedLengthTuple<T, L, Append<T, R>>;
}[R["length"] extends L ? 0 : 1];

// 本題の関数定義
type Options<T, L extends number> = {
    list: readonly T[];
    length: L;
};

function sampleWithoutReplacement<T, L extends number>({
    list,
    length,
}: Options<T, L>): FixedLengthTuple<T, L> {
    const copiedList = list.slice();
    const result = [];

    for (let i = 0; i < length; i++) {
        const index = Math.floor(Math.random() * copiedList.length);
        result.push(...copiedList.splice(index, 1));
    }

    return result as any;
}

解説

型関数について

Append<T, U extends unknown[]>は配列型の先頭要素に新たな型を追加する型関数。(参考はこちら)

FixedLengthTuple<T, L extends number, R extends any[] = []>は指定した型の固定長タプル型を生成する型関数。(参考はこちら)

本題の関数について

引数のlistのコピーを取ります。(後に配列に対して破壊的メソッドを使用するため)
結果を格納しておく空の配列resultを宣言しておきます。

lengthで指定された回数だけfor文を回します。
for文の中では、まずMath.random()を利用して配列のindexをランダムに決めます。続いて、Array.prototype.splice()を使用して配列の指定箇所から要素を抜き出します。
Array.prototype.splice()が破壊的メソッドであることを利用して「非復元性」を表現しています。

結果を格納しているresultは固定長であることをコンパイラが認識しないので、as anyでTypeScriptコンパイラを黙らせます。(プログラマーの勝利)

動作確認

こんなのでいいのかわかりませんが、結果にばらつきはなさそうです。(統計的検定までは勘弁)

const test: any = {};

for (let i = 0; i < 100000; i++) {
    const numbers = sampleWithoutReplacement({ list: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], length: 1 })

    numbers.forEach(number => {
        test[number] = test[number] ? test[number] + 1 : 1;
    })
}

console.log(test);
//{ "0": 10076, "1": 10154, "2": 9928, "3": 9998, "4": 9963, "5": 10068, "6": 9863, "7": 10024, "8": 9953, "9": 9973 } 
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0