はじめに
- TypeScript で(例えば React の JSX の中でループするような場合に)Pythonの
range()
みたいなものが欲しくなることがありますよね - 通常は
[...Array(num)].map([_, i] => ~)
みたいな感じにしているけど、初見の人にわかりにくい気がする - 簡易的な関数を作ってみたので、とりあえずメモ
実装
一応、使いまわす前提なので export
する形で記載しました。
使うところと同じファイルに書くなら export
は取り除いた方が良いかも。
export function range(start: number, stop: number, step?: number): number[];
export function range(stop: number): number[];
export function range(...args:
| [start: number, stop: number, step?: number]
| [stop: number]
): number[] {
if (args.length === 1) {
const [stop] = args;
return Array.from({length: stop}, (_, i) => i);
} else {
const [start, stop, step = 1] = args;
if (step === 0) {
throw new Exception(`range(${args})`);
}
return Array.from(
{ length: Math.ceil((stop - start) / step)},
(_, i) => start + (i * step)
);
}
}
start
とか step
がいらないなら、以下だけでもいいかも。
export const range = (stop: number) => Array.from({length: stop}, (_, i) => i);
実際に配列を作ってしまうので、Python3 ではなく Python2 の range()
相当ですけどね。
間違いとか、もっと良い方法があったら教えてください。
性能対策
以下の記事にある @kawanet さんのコメントを参考に、性能対策版を考えてみた。
reduce()
を使って、配列を 2 回作らない対策をしたもの。
ついでに配列にスプレット構文で展開するよりも fill()
の方がほんのすこし速そうなので。
export function range(start: number, stop: number, step?: number): number[];
export function range(stop: number): number[];
export function range(...args:
| [start: number, stop: number, step?: number]
| [stop: number]
): number[] {
if (args.length === 1) {
const [stop] = args;
return Array(stop).fill(0).reduce((_, __, i, a) => ((a[i] = i), a), []);
} else {
const [start, stop, step = 1] = args;
if (step === 0) {
throw new Exception(`range(${args})`);
}
return (Array(Math.ceil((stop - start) / step)).fill(0)
.reduce((_, __, i, a) => ((a[i] = start + (i * step)), a), []));
}
}
start
とか step
がいらない版:
export const range = (stop: number) => Array(stop).fill(0).reduce((_, __, i, a) => ((a[i] = i), a), []);
参考
↑下の方に連番生成のサンプルコードがあります。
↑Python3の range()
のマニュアル。ちゃんと見るといろいろと違いますね。上記 TypeScript 版では start
が省略できないとか。
↑関数のオーバーロードの書き方を参考にしました。