reduce와 take는 map, filter와 같이 지연 평가를 진행한 값을 실제 연산하는 단계이다.
take도 지연 평가를 통해 값을 특정 갯수만 yield할 수 있지만, take는 몇 개 일지 모르는 리스트에서 몇 개를 사용할지 정하는 함수이기 때문에 take까지 지연평가로 할 필요는 없다.
const queryStr = obj => go(
obj,
Object.entries,
map(([k, y]) => `${k}=${v}`),
reduce((a, b) => `${a}&${b}`)
);
log(queryStr({limit: 10, offset: 10, type: 'notice'}));
아래는 동일한 코드이다.
const queryStr = pipe(
L.entries,
L.map(([k, v]) => `${k}=${v}`),
join('&'));
log(queryStr({limit: 10, offset: 10, type: 'notice'}));
const join = curry((sep = ',', iter) =>
reduce((a, b) => `${a}${sep}${b}`, iter));
function *a() {
yield 10;
yield 11;
yield 12;
yield 13;
}
log(join(' - ', a()));
const sharpJoin = join("#");
const obj = {limit : 10 , offset : 10 , type : "notice"};
for (const a in obj) {
log(sharpJoin([a , obj[a]]));
}
// limit#10
// offset#10
// type#notice
take를 통해 find 함수를 만들었다. (find는 take를 통해서 이터러블 값을 받는 함수)
find는 결국 특정값 하나를 뽑는 함수이고, find로 가기 전에 어떤 함수들을 필터 조건으로 합성해놓고 사용할 수 있다.
const users = [
{age: 32},
{age: 31},
{age: 37},
{age: 28},
{age: 25},
{age: 32},
{age: 31},
{age: 37}
];
const find = curry((f, iter) => go(
iter,
L.filter(f),
take(1),
([a]) => a));
log(find(u => u.age < 30)(users));
go(users,
L.map(u => u.age),
find(n => n < 30),
log);
const map = curry((f, iter) => go(
L.map(f, iter),
take(Infinity)
));
const map = curry(pipe(L.map, takeAll));
const filter = curry((f, iter) => {
let res = [];
iter = iter[Symbol.iterator]();
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
if (f(a)) res.push(a);
}
return res;
});
const filter = curry(pipe(L.filter, takeAll));
값을 펼쳐서 하나의 배열로 만들게 하면서 동작지연을 수행하는 함수
const isIterable = a => a && a[Symbol.iterator];
//a가 Symbol.iterator를 가지고 있는지
L.flatten = function* (iter) {
for (const a of iter) {
if (isIterable(a)) for (const b of a) yield b
else yield a;
}
};
//출력해보기
var it = L.flatten([[1, 2], 3, 4, [5, 6], [7, 8, 9]]);
log(it.next());
log(it.next());
log(it.next());
log(it.next());
log(take(6, L.flatten([[1, 2], 3, 4, [5, 6], [7, 8, 9]])));
const flatten = pipe(L.flatten, takeAll);
log(flatten([[1, 2], 3, 4, [5, 6], [7, 8, 9]]));
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;
}
};
만일 깊은 Iterable을 모두 펼치고 싶다면 아래와 같이 L.deepFlat
을 구현하여 사용할 수 있다. L.deepFlat
은 깊은 Iterable을 펼쳐준다.
L.deepFlat = function* f(iter) {
for (const a of iter) {
if (isIterable(a)) yield* f(a);
else yield a;
}
};
log([...L.deepFlat([1, [2, [3, 4], [[5]]]])]);
// [1, 2, 3, 4, 5];
flatMap : map과 flatten을 동시에 하는 함수
log([[1, 2], [3, 4], [5, 6, 7]].flatMap(a => a)); //자바스크립트에 기본으로 있는 flatMap
log([[1, 2], [3, 4], [5, 6, 7]].flatMap(a => a.map(a => a * a)));
log(flatten([[1, 2], [3, 4], [5, 6, 7]].map(a => a.map(a => a * a))));
log(flatten([[1, 2], [3, 4], [5, 6, 7]].map(a => a.map(a => a * a))));
map과 flatten이 조금 비효율적으로 동작하기 때문에 사용한다.
L.flatMap = curry(pipe(L.map, L.flatten));
const flatMap = curry(pipe(L.map, flatten));
// var it = L.flatMap(map(a => a * a), [[1, 2], [3, 4], [5, 6, 7]]);
// 는 아래와 동일한 코드이다.
var it = L.flatMap(a => a, [[1, 2], [3, 4], [5, 6, 7]]);
log([...it]);
log(flatMap(a => a, [[1, 2], [3, 4], [5, 6, 7]]));
log(flatMap(L.range, map(a => a + 1, [1, 2, 3])));
var it = L.flatMap(L.range, map(a => a + 1, [1, 2, 3]));
log(it.next());
log(it.next());
log(it.next());
log(it.next());
log(take(3, L.flatMap(L.range, map(a => a + 1, [1, 2, 3]))));
const arr = [
[1, 2],
[3, 4, 5],
[6, 7, 8],
[9, 10]
];
go(arr,
L.flatten,
L.filter(a => a % 2), // 홀수만 남기기
L.map(a => a * a), // 제곱
take(4), // 앞에서부터 4개만 뽑아서
reduce(add), // 합산
log);
var users = [
{
name: 'a', age: 21, family: [
{name: 'a1', age: 53}, {name: 'a2', age: 47},
{name: 'a3', age: 16}, {name: 'a4', age: 15}
]
},
{
name: 'b', age: 24, family: [
{name: 'b1', age: 58}, {name: 'b2', age: 51},
{name: 'b3', age: 19}, {name: 'b4', age: 22}
]
},
{
name: 'c', age: 31, family: [
{name: 'c1', age: 64}, {name: 'c2', age: 62}
]
},
{
name: 'd', age: 20, family: [
{name: 'd1', age: 42}, {name: 'd2', age: 42},
{name: 'd3', age: 11}, {name: 'd4', age: 7}
]
}
];
go(users,
L.map(u => u.family),
L.flatten,
takeAll,
log);
go(users,
L.map(u => u.family),
L.flatten,
L.filter(u => u.age < 20),
L.map(u => u.name),
take(4),
log);
go(users,
L.flatMap(u => u.family),
L.filter(u => u.age > 20),
L.map(u => u.age),
take(4),
reduce(add),
log);