Map함수의 구조

  • 함수형 프로그래밍에서는 인자return값으로 소통하는 것을 권장
  • 인자는 보조 함수를 넣어서 사용
  • return 값을 통해 다른 곳에서 사용
const map = (fun, iter) => {
  const result = [];

  for (const a of iter) {
    // 수집할 값을 fun 함수를 통해 다양한 결과 값이 나올 수 있도록,
    // map함수 사용하는 사람에게 위임
    result.push(fun(a));
  }
};

이름만 추출

const products = [
  { name: '반팔티', price: 15000 },
  { name: '긴팔티', price: 20000 },
  { name: '핸드폰케이스', price: 15000 },
  { name: '후드티', price: 30000 },
  { name: '바지', price: 25000 },
];
  • Map 함수 사용X
const names = [];

for (const p of products) {
  names.push(p.name);
}
  • Map함수 사용O
map(p => p.name, products));

가격만 추출

const products = [
  { name: '반팔티', price: 15000 },
  { name: '긴팔티', price: 20000 },
  { name: '핸드폰케이스', price: 15000 },
  { name: '후드티', price: 30000 },
  { name: '바지', price: 25000 },
];
  • Map 함수 사용X
const prices = [];

for (const p of products) {
  prices.push(p.price);
}
  • Map함수 사용O
map(p => p.price, products));


이터러블 프로토콜을 따른 map의 다형성 1

일반 Map함수는 Array만 순회 가능

  • document.querySelectorAllArray를 상속받은 객체가 아니기 때문에 map함수를 사용할 수 없음
console.log([1,2,3].map(a => a+1));  // [2,3,4]

// .map is not a function
console.log(
	document.querySelectorAll('*').map(el => el.nodeName);
)

하지만 밑에서 만든 Map함수는 이터러블도 사용 가능

  • document.querySelectorAll이 이터러블 프로토콜을 따르기 때문에
  • 밑에 Map사용 가능
  • 즉, 밑에 작성한 Map함수는 배열 뿐만아니라 이터러블 프로토콜을 따르는 것들도 순회가 가능함
const map = (fun, iter) => {
	const result = [];

	for(const a of iter) {
		result.push(fun(a));
	}
}

// ["HTML", "HEAD", "SCRIPT", "SCRIPT", "BODY", "SCRIPT"]
console.log(
	map(el => el.nodeName, document.querySelectorAll('*'));
)

const it = document.querySelectorAll('*')[Symbol.iterator]();
console.log(it.next());   // {vale:html, done:false}
console.log(it.next());   // {vale:head, done:false}

또한 제너레이터에서도 사용 가능

function* gen() {
  yield 2;
  yield 3;
  yield 4;
}

log(map((a) => a * a, gen())); //
  • 제너레이터 안에 코드 문장도 사용 가능
function* gen() {
  yield 2;
  if (false) yield 3;
  yield 4;
}

log(map((a) => a * a, gen()));

중요

  • 웹 API도 ECMAScript의 이터러블 프로토콜을 따르고 있기 때문에, 다양한 조합성을 갖을 수 있음
  • 클래스, 프로토타입의 뿌리로 가진 카테고리 안에 있는 값만 사용할 수 있는 것보다, 위의 방법이 유연하고 다형성이 높음


이터러블 프로토콜을 따른 map의 다형성 2

Map 객체를 새로 만들 수 있는 다형성

let m = new Map();

m.set('a', 10);
m.set('b', 20);

const it = m[Symbol.iterator]();

console.log(it.next()); // {value:['a', 10], done: false}
console.log(it.next()); // {value:['b', 20], done: false}

log(map(([k, a]) => [k, a * 2], m)); // [['a', 20], ['b', 40]]

// map함수를 통해 새로운 값으로 map 객체를 만들 수 있음
// // {"a" => 20, "b" => 40}
log(new Map(map(([k, a]) => [k, a * 2], m)));

느낀점

map 함수의 내부 구조를 알게되었으며, 이터러블 프로토콜을 따르는 것들을 순회하여 새로운 다양한 조합을 만들 수 있다는 사실을 알게되었습니다. 또한 이러한 map함수를 사용하면 코드 가독성 측면이나 성능 측면에 좋다는 것도 알게 되었습니다.


참고

유인동님의 함수형 프로그래밍과 JS ES6+ 강의