L.flatten 함수
flatten이라는 함수를 만들고자 한다. 해당 함수는 임의로 배열이 섞여 있는 배열을 하나의 배열로 전환해주는 함수다. 또한, 지연 평가를 적용할 수 있는 함수다.
예를 들어, 아래와 같은 배열이 있다고 한다면
[[1, 2], 3, 4, [5, 6], [7, 8, 9]];
이를 하나의 배열로 처리해 아래와 같이 만들어 줄 수 있다.
[1, 2, 3, 4, 5, 6, 7, 8, 9]
이제 본격적으로 함수를 생성한다.
const isIterable = a => a && a[Symole.iterator];
L.flatten = function *(iter) {
for (const a of iter) {
if (isIterable(a)) {} for (const b of a) yield b;
else yield a;
}
};
const it = L.flatten([[1, 2], 3, 4, [5, 6], [7, 8, 9]]);
console.log(it.next());
// {value: 1, done: false}
이처럼 L.flatten은 배열을 원소로 가지는 배열을 받아서 하나의 배열로 만들어 주는 함수이며, 이터레이터를 반환한다.
L.flatten이 있으면 즉시 평가하는 flatten도 쉽게 만들 수 있다.
이전에 만들었던 함수들
const curry = f =>
(a, ...fs) => fs.length
? f(a, ...fs)
: (...fs) => f(a, ...fs)
const reduce = curry((f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
for (const a of iter) {
acc = f(acc, a)
}
return acc;
};
const go = (...fs) => reduce((a, f) => f(a), ...fs);
const pipe = (...fs) => a => go(a, ...fs);
const take = (l, iter) => {
const res = [];
for (const a of iter) {
res.push(a);
if (res.length === l) return res;
}
return res;
};
const takeAll = take(Infinity);
const flatten = pipe(L.flatten, takeAll);
또한, 이터러블한 이터레이터를 반환하므로 아래와 같이 쓸 수도 있다.
console.log(take(6, L.flatten([[1, 2], 3, 4, [5, 6], [7, 8, 9]]));
yield *
yield *
을 활용해서 아래와 같이 코드를 바꿀 수 있다. yield *iterable
은 for (const val of iterable) yield val;
과 같다.
L.flatten = function *(iter) {
for (const a of iter) {
if (isIterable(a)) yield *a;
else yield a;
}
};
L.deepFlat
더 깊은 iterable을 모두 펼치고자 한다면 아래와 같이 L.deepFlat을 구현할 수 있다.
L.deepFlat = function *f(iter) {
for (const a of iter) {
if (isIterable(a)) yield *f(a);
else yield a;
}
};
console.log([...L.deepFlat([1, [2, [3, 4], [[5]]]])]);
// [1, 2, 3, 4, 5];
이부분은 이전에 배웠던 함수의 연속이라 크게 와닿지 않았다. 그런데, yield *이나 deepFlat 부분은 새로 알 수 있어서 좋았다. yield * 패턴은 특히 깊은 배열을 풀어서 쓰는 데 유용해서 좋았다. 애스터리스크가 이터러블한 이터레이터를 반환하는 것이기 때문에 이렇게 반환하는 것 같다. 그리고 deepFlat의 경우 재귀적으로 작용하는 것으로 파악된다. 그래서 배열의 depth가 아무리 길다고 하더라도 모든 값들을 뽑아낼 수 있는 것 같다. 새로 배울 수 있는 점이라서 좋았다. 앞으로 제너레이터와 비동기적인 작동이 이어질 텐데 어떻게 효율적으로 코드를 작성할 수 있을지 기대된다.
출처: 인프런 함수형 프로그래밍과 JavaScript ES6+
'프론트엔드 > JS' 카테고리의 다른 글
[JavaScript] 이터러블 중심 프로그래밍 실무적인 코드 (0) | 2023.05.11 |
---|---|
[JavaScript] L.flatMap, flatMap (0) | 2023.05.10 |
[JavaScript] L.map, L.filter로 map과 filter 만들기 (0) | 2023.05.09 |
[JavaScript] 함수 합성을 통해 find 함수 만들기 (0) | 2023.05.08 |
[JavaScript] Array.prototype.join 보다 다형성 높은 join 함수 (0) | 2023.05.08 |