Open mochang2 opened 1 year ago
const users = [
{ name: "AA", age: 35 },
{ name: "BB", age: 26 },
{ name: "CC", age: 28 },
{ name: "CC", age: 34 },
{ name: "EE", age: 23 },
];
console.log(_.reduce((total, u) => total + u.age, 0, users));
위와 같은 형식(user에는 age property가 존재한다는 것을 알아야 함) 보다는 아래와 같은 형식.
reduce의 초기값을 주지 않는 형태가 일반적으로 덜 복잡함.
const users = [
{ name: "AA", age: 35 },
{ name: "BB", age: 26 },
{ name: "CC", age: 28 },
{ name: "CC", age: 34 },
{ name: "EE", age: 23 },
];
const add = (a, b) => a + b;
const getAges = L.map((u) => u.age);
console.log(_.reduce(add, getAges(users)));
만약 유저 조건이 필요하다면 중간에 filter를 추가하면 됨.
모나드적 개념을 활용하면 안전하게 합성할 수 있음(아래 예시와 같은 경우 fg의 인자가 숫자가 아니어도).
(사실 인터프리터 언어여서 생기는 문제기도 함)
const f = (x) => x + 10;
const g = (x) => x - 5;
const fg = (x) => f(g(x));
_.go(10, fg, console.log); // 15
_.go(undefined, fg, console.log); // NaN
_.go([], L.map(fg), _.each(console.log)); // 15
_.go([10], L.map(fg), _.each(console.log)); // 15
find 대신 filter(L.filter) 쓰는 방법도 하나의 방법임.
const users = [
{ name: "AA", age: 35 },
// { name: 'BB', age: 26 },
{ name: "CC", age: 28 },
{ name: "DD", age: 34 },
{ name: "EE", age: 23 },
];
const user = _.find((u) => u.name == "BB", users);
if (user) {
// undefined가 아닐 경우에 대한 조건문이 추가되어야 함
console.log(user.age);
}
_.go(
users,
L.filter((u) => u.name == "BB"),
L.map((u) => u.age),
L.take(1),
_.each(console.log)
);
L.keys = function* (obj) {
for (const k in obj) {
yield k;
}
};
L.values = function* (obj) {
for (const k in obj) {
yield obj[k];
}
};
L.entries = function* (obj) {
for (const k in obj) {
yield [k, obj[k]];
}
};
const object = (entries) =>
_.go(
entries,
L.map(([k, v]) => ({ [k]: v })),
_.reduce(Object.assign)
);
모델 설계는 OOP 적으로 해도, 그 안에 메서드는 결국 로직이 필요함.
해당 로직을 if는 filter로, 변형은 map으로, 결과는 take이나 reduce로 대체할 수 있음.
결론: OOP와 섞어서 쓸 수 있는 개념임. 대체재가 아님.
class Model {
constructor(attrs = {}) {
this._attrs = attrs;
}
get(k) {
return this._attrs[k];
}
set(k, v) {
this._attrs[k] = v;
return this;
}
}
class Collection {
constructor(models = []) {
this._models = models;
}
at(idx) {
return this._models[idx];
}
add(model) {
this._models.push(model);
return this;
}
*[Symbol.iterator]() {
yield* this._models;
// 또는
// for (const model of this._models) {
// yield model;
// }
}
// 또는
// [Symbol.iterator]() {
// return this._models[Symbol.iterator]();
// }
}
const coll = new Collection();
coll.add(new Model({ id: 1, name: "AA" }));
coll.add(new Model({ id: 3, name: "BB" }));
coll.add(new Model({ id: 5, name: "CC" }));
console.log(coll.at(2).get("name")); // CC
console.log(coll.at(1).get("id")); // 3
_.go(
coll,
L.map((m) => m.get("name")),
_.each(console.log)
);
_.go(
coll,
_.each((m) => m.set("name", m.get("name").toLowerCase()))
_.each(console.log) // aa, bb, cc
);
_.go(
[1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0],
L.takeWhile((a) => a), // 1 ~ 8까지만
_.each(console.log)
);
_.go(
[1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0],
L.takeUntil((a) => a), // 1까지만
_.each(console.log)
);
_.go(
[0, false, undefined, null, 10, 20, 30],
L.takeUntil((a) => a), // 10까지만
_.each(console.log)
);
값으로 다룰 수 있어야 함.
변수에 담을 수 있어야 함. 함수의 인자로 사용될 수 있어야 함.
함수의 결과로 사용될 수 있어야 함.
이를 함수에 적용할 수 있음.
함수를 값으로 다룸(JS는 함수가 일급이여서 가능함).
Array
,Set
,Map
등현재 위 코드를 사용해서 원하는 값을 표현하려면 뒤에서부터 읽어야 함.
예를 들어 가격이 20000원 미만인 상품들의 가격의 합을 구하고 싶다면
reduce((a, b) => a + b, map((product) => product.price, filter(product => product.price < 20000, products)))
와 같은 식으로 말이다.가독성 좋게 이를 바꿔보고자 함.
단순히 콜백 지옥을 해결하는 기능이 아님.
함수형 프로그래밍 관점에서 볼 때 비동기를 일급으로 다룰 수 있게 해줌.
콜백은 내부 컨텍스트에서 외부의 실행 결과를 받아서야만 지속적으로 실행할 수 있고,
Promise
는 실행 결과를 외부에서도 받아 지속적으로 실행할 수 있다.