map
•
이터러블 프로토콜을 따르는 함수
•
Iterable 을 순회하며 각각의 값을 function 'f'를 이용해 수행후 반환한다.
/**
* @param {function=} f
* @param {iterable=} iter
*/
const map = (f, iter) =>{
let res = [];
for (const i of iter){
res.push(f(i));
}
return res;
}
JavaScript
복사
이터러블 프로토콜을 따른 map의 다형성
•
iterable protocol을 따르는 객체들은 모두 map을 사용가능하다.
•
map함수는 iterable protocol을 따르는 for문을 사용하여 순회를 할 수 있다.
querySelectorAll은 map이 동작할까?
•
NodeList에는 map이 따로 정의되어있지 않기 때문에 동작하지 않는다.
document.querySlectorAll("*").map(...); //error function is not defined (map)
JavaScript
복사
하지만, iterable protocol을 따르기 때문에 순회로직 사용가능.
const map = (f, iter) =>{
let res = [];
for (const i of iter){
res.push(f(i));
}
return res;
}
console.log(map(el=>el.nodeName, document.querySelectorAll('*')));
//["HTML", "HEAD", "SCRIPT", "SCRIPT", "BODY"...]
function *gen(){
yield 2;
yield 3;
yield 4;
}
console.loglog(map(a=>a*a, gen()); // 4, 9, 16
JavaScript
복사
Iterable Protocol을 따를경우 map을 사용할 수 있다. →Iterable Protocol을 따르는 함수들을 사용하는 것은 다른 함수들과의 조합성이 좋아지고 유연성, 다형성이 올라간다고 할 수 있다.
Iterable Protocol을 따랐을 때의 조합성 사례
let m = new Map();
m.set('a', 10);
m.set('b', 20);
const it = m[Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());
JavaScript
복사
filter
개요
⇒ 배열데이터가 있을때 특정 조건에 맞는 데이터만 추려내는 함수
예제 코드
•
요구사항
⇒ 3만원 미만인 책의 리스트만 추출하라.
•
책 원천 데이터
const books = [
{name:"자바의정석", price:40000},
{name:"클린아키텍처", price:28000},
{name:"SQL첫걸음", price:22000},
{name:"MFC윈도우프로그래밍", price:35000},
{name:"토비의스프링", price:98000},
{name:"친절한SQL튜닝", price:50000},
];
JSON
복사
•
script
let under30000 = [];
for (const book of books) {
if (book.price < 30000) {
under30000.push(book);
}
}
console.log(under30000);
/* 실행 결과
(2) [{…}, {…}]
0: {name: "클린아키텍처", price: 28000}
1: {name: "SQL첫걸음", price: 22000}
length: 2
__proto__: Array(0)
*/
JavaScript
복사
•
filter 리팩토링
const filter = (f, iter) =>{
let res = [];
for (const a of iter) {
if(f(a))res.push(a);
}
return res;
}
JavaScript
복사
reduce
개요
배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고 하나의 결과값을 반환하는 함수입니다.
배열을 하나의 값으로 축약할 때 사용하는데 이를테면 [1,2,3,4] 라는 배열이 있을 때 reduce를 통해서 얻을 수 있는 기대값은 10입니다.
예제 코드
•
요구사항
⇒ 배열 [1,2,3,4,5]의 값을 축약한 결과값을 추출하는 함수를 만들어라.
•
reduce함수 없이 구현
const nums = [1,2,3,4,5];
let totla = 0;
for (const n of nums) {
total = total + n;
}
console.log(total) // 15
JavaScript
복사
•
reduce 함수를 구현
const reduce = (f, acc, iter) =>{
for (const a of iter){
acc = f(acc, a);
}
return acc;
}
console.log(reduce(acc, 0, nums)); // 15
JavaScript
복사
◦
acc값을 꼭 사용하지 않아도 동작하도록 로직을 강화해볼 수 있습니다.
•
reduce 로직 강화
const reduce = (f, acc, iter) =>{
if(!iter){
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const a of iter){
acc = f(acc, a);
}
return acc;
}
console.log(reduce(acc, nums)); // 15
JavaScript
복사
◦
reduce 함수의 인자값으로 초기값(acc)를 주지 않고 바로 함수(f)와 배열(iter)만 넘겨줘도 동작하도록 조건문을 추가해줬습니다.
•
reduce함수 응용
⇒ 배열뿐 아니라 객체 배열의 조작도 인자로 넘어가는 f 를 어떻게 구현하느냐에 따라 가능해집니다.
위에서 정의했던 책들의 원천데이터를 가지고 reduce함수를 통해 책들의 총 가격을 계산해봅시다.
const books = [
{name:"자바의정석", price:40000},
{name:"클린아키텍처", price:28000},
{name:"SQL첫걸음", price:22000},
{name:"MFC윈도우프로그래밍", price:35000},
{name:"토비의스프링", price:98000},
{name:"친절한SQL튜닝", price:50000},
];
const reduce = (f, acc, iter) =>{
if(!iter){
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const a of iter){
acc = f(acc, a);
}
return acc;
}
log(reduce((total_price, product)=> total_price+=product.price,0 , books)); //273000
JavaScript
복사
◦
초기값 acc를 total_price로 , books의 book을 인자값으로 받아서 합연산을 해주는 함수를 인자로 넘겨서 합을 도출합니다.
결론
⇒ 이렇게 정의한 reduce 함수의 장점은 함수의 교체가 가능하다는점에 있습니다.
현재는 add 함수를 통해 배열의 덧셈을 해줬지만, 다른 함수를 통해 다른식의 결과 도출 역시 가능해집니다.
map+filter + reduce 중첩 사용과 함수형 사고
위에서 배운 map, filter, reduce를 중첩해서 사용할 수 있습니다.
예제 코드
•
요구사항
⇒ 책의 원천데이터들 (books)중에서 30,000원 이하의 책들의 총 합 가격은 얼마인지 구하시오.
•
books data
const books = [
{name:"자바의정석", price:40000},
{name:"클린아키텍처", price:28000},
{name:"SQL첫걸음", price:22000},
{name:"MFC윈도우프로그래밍", price:35000},
{name:"토비의스프링", price:98000},
{name:"친절한SQL튜닝", price:50000},
];
JSON
복사
•
map, filter, reduce 함수
const map = (f, iter) => {
let res = [];
for (const i of iter) {
res.push(f(i));
}
return res;
}
const filter = (f, iter) => {
let res = [];
for (const a of iter) {
if (f(a)) res.push(a);
}
return res;
}
const reduce = (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 add = (a, b) => a + b;
JavaScript
복사
•
3가지 함수의 중첩 사용 코드
log(reduce(add, 0, map(p => p.price, filter(p => p.price < 30000, books)))); //50000
JavaScript
복사