2025年の5月にIterator HelperがBaselineに乗ったそうです。
Iteratorのことを知ればもっとプログラミングが面白くなるでしょう。
今回はTypeScriptをメインに話を進めますがIteratorの考え方はどの言語でも持ち込めるのでぜひ。
詳しい基本理念を説明しようよ思ったんですが思った以上に難しかったためこの記事は好きなIteratorのメソッド発表ドラゴンさんが執筆致します。
コード自体はTypeScriptで書きますがTypeScriptに限らずいろんな言語のメソッドを想像で書きます。
map (Iterator<I> -> fn(i: I): O -> Iterator<O>)
王道の王道です。
アイテムを一つ一つ変換していきます。
const lines = [ "1,taro", "2,hanako", "3,neko" ];
const data = lines[Symbol.iterator]()
.map((line) => line.split(","))
.map(([id, name]) => ({
id,
name
}));
console.log(data); // [ { id: '1', name: 'taro' }, { id: '2', name: 'hanako' }, { id: '3', name: 'neko' } ]
filter (Iterator<I> -> fn(i: I): boolean -> Iterator<I>)
条件に一致するアイテムだけを通します。
const numbers = [ 1, 2, 3, 4, 5 ];
const evens = numbers[Symbol.iterator]()
.filter((n) => n % 2 === 0);
console.log(evens); // [ 2, 4 ]
flat (Iterator<Iterable<I>> -> Iterator<I>)
入れ子になっているIterableを展開します。
const boxes = [ ["1", "2"], ["3"], ["4", "5"] ];
// 箱から中身をぶちまけて一列にする
const fruits = boxes[Symbol.iterator]()
.flat();
console.log(fruits); // [ "1", "2", "3", "4", "5" ]
find (Iterator<I> -> fn(i: I): boolean -> I | undefined)
条件に一致するIを1 or 0こ返します。
ID検索をするときとかに有用です。
条件に一致しない可能性もあるため、I | undefined
const users = [
{ id: 1, name: 'taro' },
{ id: 2, name: 'hanako' },
{ id: 3, name: 'neko' }
];
const target = users[Symbol.iterator]()
.find((user) => user.id === 2);
console.log(target); // { id: 2, name: 'hanako' }
reduce (Iterator<I> -> fn(acc: A, cur: I): A -> A)
一つの結果にまとめるやつ
const prices = [ 100, 200, 300 ];
const total = prices[Symbol.iterator]()
.reduce((acc, price) => acc + price, 0);
console.log(total); // 600
every (Iterator<I> -> fn(i: I): boolean -> boolean)
一つでも要素を調べていく過程でfnがfalseを返せばfalseを変えます。
// 全てが80以上だと
const scores = [ 80, 95, 100 ];
const isAllPassed = scores[Symbol.iterator]()
.every((score) => score >= 80);
console.log(isAllPassed); // true
// 80未満が一つでもあると、、、
const scores = [ 70, 95, 100 ];
const isAllPassed = scores[Symbol.iterator]()
.every((score) => score >= 80);
console.log(isAllPassed); // false
some (Iterator<I> -> fn(i: I): boolean -> boolean)
fnが一つでもtrueを返すとそこで探索をやめ、trueを返します。
// 一つでも80以上があると
const scores = [ 90, 2, 10 ];
const isSomePassed = scores[Symbol.iterator]()
.some((score) => score >= 80);
console.log(isSomePassed); // true
// 80以上が一つもないと
const scores = [ 70, 2, 12 ];
const isSomePassed = scores[Symbol.iterator]()
.some((score) => score >= 80);
console.log(isSomePassed); // false
zip (Iterator<A> -> Iterable<B> -> Iterator<[A, B]>)
データの順序は保証されているがそれぞれ別の配列にデータが有るとき。
お互いのiteratorが一致しない場合長いの方のはみ出し分はカットされる。
const headers = [ "id", "name", "role" ];
const values = [ 1, "taro", "admin", "extra" ];
const entries = headers[Symbol.iterator]()
.zip(values[Symbol.iterator]());
console.log(entries); // [ ["id", 1], ["name", "taro"], ["role", "admin"] ]
take (Iterator<I> -> count: number -> Iterator<I>)
指定した数の分だけ先頭から取ってくる。
n個指定したからといって、必ずn個来る保証は無い。
const ranking = [ "1位", "2位", "3位", "4位", "5位" ];
const top3 = ranking[Symbol.iterator]()
.take(3);
console.log(top3); // [ "1位", "2位", "3位" ]
drop (Iterator<I> -> count: number -> Iterator<I>)
takeの逆です。
n個進めたあとのIteratorを返してくれます。
const ranking = [ "1位", "2位", "3位", "4位", "5位" ];
const outTop3 = ranking[Symbol.iterator]()
.drop(3);
console.log(outTop3); // [ "4位", "5位" ]
終わりに
私が使ったことのあるメソッドたちを書いて見ました。
今回はTypeScriptで説明をしましたが、Iteratorを好きになったきっかけはRustを触ったことでした。
RustのIteratorには多種多様なnextを応用したメソッドがあり好奇心かき立たされると同時に、TypeScriptのIteratorの不十分さで少し残念な気持ちになっていました。
しかし、記事冒頭にも書いた通りただのInterfaceからIterator Helperとして機能が増えており非常に嬉しかったです。