#1 사용자 정의 객체를 다루기
map, set 다루기
자바스크립트의 기본객체가 아닌 추가적으로 만들어인 사용자 정의 내장 객체 역시도 이터러블로 프로그래밍을 다룰 수 있다.
먼저 Map을 알아보자.
let m = new Map();
m.set('a', 1);
m.set('b', 2);
m.set('c', 3);
위와 같은 Map도 이터러블로 프로그래밍 하고 다시 새로운 Map으로 생성이 가능하다.
_.go(
m,
_.filter(([k, v]) => v % 2),
entries => new Map(entries),
console.log);
// Map(2) {"a" => 1, "c" => 3}
이어서 Set같은 경우도 마찬가지이다.
let s = new Set();
s.add(10);
s.add(20);
s.add(30);
console.log([...s]); // [10, 20, 30]
const add = (a, b) => a + b;
console.log(_.reduce(add, s)); // 60
Model, Collection 클래스 만들어서 이터러블 프로토콜 지원하기
Model, Colletion 클래스를 만들어서 사용자 정의 객체를 만들 것인데, 여기서 이터러블을 지원하도록 만들어 볼 것이다.
먼저, 다음과 같이 동작하는 Model과 Collection 클래스가 있다.
class Model{
constructor(attrs = {}){
this._attrs = attrs;
}
get(k){
return this._attrs[k] = v;
}
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;
}
}
이 Model과 Collection을 일반적인 객체지향적 설계로 사용해보자.
일단 아래 코드처럼 coll을 통해 새로운 모델 3개를 생성했다.
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
하지만, 위 코드는 생성된 모델배열을 순회하는 코드를 짜려면 아래처럼 range처럼 범위를 직접 지정해주어야 가능하다.
_.go(
L.range(3),
L.map(i => coll.at(i)),
L.map(m => m.get('name')),
_.each(console.log)); // AA BB CC
위와 같이 만들어 지는 것은, range라는 범위 숫자에 의존해야하며, 뒤떨어진 코드가 된다.
이를 이터러블 측면에서 순회되는 코드로 한번 바꿔보자.
Collection 클래스에 Symbol.iterator가 Model전체를 뽑아주는 역할을 해주게끔 하면 된다.
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;
}
/*위와 같은 동작
[Symbol.iterator](){
return this._models[Symbol.iterator]();
}
*/
}
위와 같이 짜준다면 coll 을통해 이터러블 순회하면서, 동작을 할 수 있다.
_.go(
coll,
L.map(m => m.get('name')),
_.each(console.log)); // AA BB CC
/* 소문자로 id값 변화*/
_.go(
coll,
_.each(m => m.set('name', m.get('name').toLowerCase())));
_.go(
coll,
L.map(m => m.get('name')),
_.each(console.log)); // aa bb cc
Product, Products 클래스로 메서드를 함수형으로 구현하기
이번엔 Model, Colletion 클래스를 상속받은 Product와 Products 클래스를 만들고 그에 어울리는 메소드를 만들며 어떻게 이터러블 프로토콜과 조화를 이룰 수 있는지 알아볼 것이다.
class Product extends Model{}
class Products extends Collection{}
각각 상속받은 Product와 Products가 있다. 이 코드에서 상품 몇개를 추가하고 값을 더하는 일반적인 코드를 만들어 보자.
class Product extends Model{}
class Products extends Collection{
totalPrice() {
let total = 0;
this._models.forEach(product =>{
total += product.get('price');
});
return total;
}
}
const products = new Products();
products.add(new Product({ id: 1, price: 10000}));
products.add(new Product({ id: 3, price: 25000}));
products.add(new Product({ id: 5, price: 35000}));
console.log(products.totalPrice()); // 70000
이를 이터러블 프로그래밍에 더 어울리고 간결하게 해보자.
Products에서 this를 통해 이터러블이 순회할 수 있는지 확인해보면 다음과 같다.
class Products extends Collection{
totalPrice() {
console.log([...this]); // (3) [Product, Product, Product]
let total = 0;
this._models.forEach(product =>{
total += product.get('price');
});
return total;
}
}
위 Collection에서 Symbol.iterator를 구성했기 때문에 가능한 것인데, 이를 토대로 아래처럼 코드를 정리해줄 수 있다.
class Product extends Model{}
class Products extends Collection{
totalPrice() {
return _.go(
this,
L.map(p => p.get('price')),
_.reduce((a, b) => a + b));
}
}
const products = new Products();
products.add(new Product({ id: 1, price: 10000}));
products.add(new Product({ id: 3, price: 25000}));
products.add(new Product({ id: 5, price: 35000}));
console.log(products.totalPrice()); // 70000
'Web[웹] > ES6+ 함수형 언어' 카테고리의 다른 글
[ES6+: 응용] 프론트엔드에서 활용하기 (1) (0) | 2019.11.04 |
---|---|
[ES6+: 응용] 시간을 이터러블로 다루기 (0) | 2019.11.01 |
[ES6+: 응용] 객체를 이터러블 프로그래밍으로 다루기 (0) | 2019.10.29 |
[ES6+: 응용] map과 filter로 하는 안전한 함수 합성 (0) | 2019.10.29 |
[ES6+: 응용] reduce함수를 통해보는 명령형 습관을 지우기 (0) | 2019.10.25 |