◆ 最終的に欲しい数だけ処理するのでムダを減らせる
◆ LINQ 風なメソッドチェーン版もあり

配列をメソッドチェーンで map して filter するときって 一旦 map で新しい配列を作ってそれを filter という流れになっています
また map や filter の結果のうち 最初の数個しか要らないといった場合もありますが 要らない部分も処理しています
配列が長くなるとムダが多いと感じるところです

.NET の LINQ みたいに遅延評価にしたいなと思ったので 普段あんまり使わないジェネレータで作ってみました

const gen = (len) => {
return function* () {
for (let i = 0; i < ~~len; i++) {
yield i
}
}
}

const filter = (fn) => {
return function* (it) {
for (const x of it) {
if (fn(x)) yield x
}
}
}

const map = (fn) => {
return function* (it) {
for (const x of it) {
yield fn(x)
}
}
}

const it = [
gen(10),
filter((x) => x % 2),
map((x) => x * 10),
].reduce((a, b) => b(a), null)

console.log([...it])
// [10, 30, 50, 70, 90]

0 ~ 9 を奇数のみに filter して 10 倍する map を通しています

遅延評価らしさを出すために無限ループのカウンターからフィルタした結果を指定数取得してみます

const infinity = () => {
return function* () {
let i = 0
while (true) {
yield i++
}
}
}

const take = (num) => {
return function* (it) {
let i = 0
for (const x of it) {
yield x
if (++i === num) {
return
}
}
}
}

const it2 = [
infinity(),
filter((x) => x % 2),
take(10),
].reduce((a, b) => b(a), null)

console.log([...it2])
// [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

奇数を 10 件取得できました

reduce を毎回自分で書くのは疲れるので 関数にしてしまったほうが良いかもです

const compose = arr => init => {
return arr.reduce((a, b) => b(a), init)
}

const get3Greater10 = compose([
filter((x) => x > 10),
take(3),
])

for(const x of get3Greater10([1, 200, 3, 101, 44, 80, 900, 8, 80])) {
console.log(x)
}
// 200
// 101
// 44

参考と言った LINQ に合わせてメソッドチェーン形式にもしてみました

class Chain {
chain = []
init = null

constructor(init) {
this.init = init
}

where(fn) {
this.chain.push(function* (it) {
for (const x of it) {
if (fn(x)) yield x
}
})
return this
}

select(fn) {
this.chain.push(function* (it) {
for (const x of it) {
yield fn(x)
}
})
return this
}

take(num) {
this.chain.push(function* (it) {
let i = 0
for (const x of it) {
yield x
if (++i === num) {
return
}
}
})
return this
}

[Symbol.iterator]() {
return this.chain.reduce((a, b) => b(a), this.init)
}
}

const from = (init) => new Chain(init)

使い方はこうなります

const it = from([1, 2, 3, 4, 5, 6, 7, 8])
.where((x) => x > 3)
.select((x) => x * 10)
.take(3)

for (const n of it) {
console.log(n)
}
// 40
// 50
// 60