[JS] map filter reduce

최정환·2021년 9월 17일
0


  const products = [
    {name: '반팔티', price: 15000},
    {name: '긴팔티', price: 20000},
    {name: '핸드폰케이스', price: 15000},
    {name: '후드티', price: 30000},
    {name: '바지', price: 25000}
  ];

map

map()는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 🔔새로운 배열을 반환한다.

arr.map(callback(currentValue[, index[, array]])[, thisArg])

  1. callback : 새로운 배열 요소를 생성하는 함수 3가지 인수를 가진다.
    (1) currentValue : 처리할 현재 요소
    (2) index : 처리할 현재 요소의 인덱스
    (3) array : map()을 호출한 배열

  2. thisArg : callback을 실행할 떄 this로 사용되는 값


🔧 rObj {}에 obj.key를 key값으로 obj.value를 value값으로 구성해서 각각의 요소에 대해 한번씩 순서대로 불러서 새로운 배열을 반환값으로 return한다.

var kvArray = [{key:1, value:10},
               {key:2, value:20},
               {key:3, value: 30}];

var reformattedArray = kvArray.map(function(obj){
   var rObj = {};
   rObj[obj.key] = obj.value;	
   return rObj;
});
// reformattedArray는 [{1:10}, {2:20}, {3:30}]

// kvArray는 그대로
// [{key:1, value:10},
//  {key:2, value:20},
//  {key:3, value: 30}]

🔧 map의 작동 방식


// f iter를 인자로 받고 f를 a인자를 받는 함수화시켜서 결과값을 res에 담는다
const map = (f, iter) => {
let res = [];
for (const a of iter){
	res.push(f(a));
}
  return res;
};


let names = [];
for (const p of products){
	names.push(p.name);
}
log(name);

log(map(p=>p.name, products));	// products에 있는 name들 출력



let prices = [];
for (const p of products){
	names.push(p.price);
}
log(name);

log(map(p=>p.price, products));	// products에 있는 price들 출력

map함수는 iterator 프로토콜을 따르는 함수들 사용이 가능하다.


function *gen(){
	yield 2;
  	yield 3;
  	yield 4;
}
log(map((a => a*a, gen());
// [4,9,16]
        
        
let m = new Map();
    m.set('a',10);
	m.set('b',20);
map(([k,a]=>[k,a*2]), m);	// ["a",20],["b",40]

const it = m[Symbol.iterator]();
log(it.next());	// value:Array(2) ["a",10]	
log(it.next());	// value:Array(2) ["b",20]	



filter

filter()는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 🔔새로운 배열로 반환한다.

arr.filter(callback(element[, index[, array]])[, thisArg])

  1. callback : 각 요소를 시험할 함수. true 반환시 요소 유지, false 반환시 버림 3가지 매개변수를 받는다.
    (1) element : 처리할 현재 요소
    (2) index : 처리할 현재 요소의 인덱스
    (3) array : filter()을 호출한 배열

  2. thisArg : callback을 실행할 때 this로 사용하는 값

🔧 indexOf는 배열에서 지정된 요소를 찾을 수 있는 첫 번째 인덱스를 반환하고 존재하지 않으면 -1을 반환

const fruits = ['apple', 'banana', 'grapes', 'mango', 'orange'];

/**
 * 검색 조건에 따른 배열 필터링(쿼리)
 */
const filterItems = (query) => {
  return fruits.filter((el) =>
    el.toLowerCase().indexOf(query.toLowerCase()) > -1
  );
}

console.log(filterItems('ap')); // ['apple', 'grapes']
console.log(filterItems('an')); // ['banana', 'mango', 'orange']



🔧 filter의 작동 방식

const filter = (f, iter) =>{
	let iter = [];
  for (const p of iter){
  	if(f(a)) res.push(a);
  }
}


let under20000 = [];
for (const p of products) {
	if(p.price < 20000) under20000.push(p);
}
log(...under20000); // 반팔티, 핸드폰케이스

log(...filter(p=>p.price < 20000, products)); // 반팔티, 핸드폰케이스

let over20000 = [];
for (const p of products) {
	if(p.price >= 20000) over20000.push(p);
}
log(...over20000); // 긴팔티, 후드티, 바지
log(...filter(p=>p.price >= 20000, products)); // 긴팔티, 후드티, 바지



reduce

reduce()는 배열의 각 요소에 대해 주어진 리듀서 함수를 실행하고 하나의 결과값을 반환한다.

arr.reduce(callback[, initialValue])

리듀서 함수는 네 개의 인자를 가진다.
1. 누산기(acc) - 누산기에 리듀서 함수의 반환 값 할당, 순회 중 유지되므로 최종 결과는 하나의 값이 된다.
2. 현재값(cur)
3. 현재 인덱스(idx)
4. 원본 배열(src)

arr.reduce(function(acc,cur,idx,src)){
           return acc + cur;
           }

🔧 reduce 이해하기 힘들었던 예제

var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

var countedNames = names.reduce(function (allNames, name) {
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

❌ 궁금했던 점
1. 왜 {} 안에 들어가 있는 걸까?
2. allNames[name]은 allNames가 배열로 만들어져있다면 순서를 내밷는게 아닌가?

✅ 해결
1. 함수 뒤에 {}는 초기값으로 name들을 쌓아두는 allName이다.
2. allNames는 배열로 안에 하나씩 집어 넣는게 아니라 {}안에 딕셔러니형식으로 하나씩 쌓인다.
따라서 allNames[name]은 value이고 name은 key이다.

allNames, name의 console.log결과값

{} Alice
{ Alice: 1 } Bob
{ Alice: 1, Bob: 1 } Tiff
{ Alice: 1, Bob: 1, Tiff: 1 } Bruce
{ Alice: 1, Bob: 1, Tiff: 1, Bruce: 1 } Alice
{ Alice: 2, Bob: 1, Tiff: 1, Bruce: 1 }

🔧 reduce의 작동 방식

const nums = [1,2,3,4,5];

let total = 0;
for (const n of nums) {
	total = total + n;
}
log(total)

const reduce = (f, acc, iter) => {
	// iter 값이 없다면
  if(!iter){
    iter = acc[Symbol.iterator]();	// iterable
      acc = iter.next().value;	// 다음 값을 초기값으로
    }
  for (const a of iter){
    acc = f(acc,a);
    }
  	return acc;
}

const add = (a,b) => a + b;
log(reduce(add, 0, [1,2,3,4,5]));	// 15
>> log(add(add(add(add(0,1),2),3),4),5); //15

log(reduce(add, [1,2,3,4,5])); 처럼 초기값을 두지 않는다면
log(reduce(add, 1, [2,3,4,5])); reduce가 내부적으로 값을 이와 같이 받은것처럼 동작한다.


log(reduce((total_price, product) => total_price + product.price, 0, products));	// 105000



map+filter+reduce

🔧 products안에 20000미만으로 filter한 값(배열)을 map으로 추출 후 reduce로 add한 값을 반환한다..

const add = (a+b) => a + b;
log(reduce(add,
           map(p=>p.price,
               filter(p=>p.price < 20000, products))));
// 30000

🔧 map으로 products안에 price값들(배열)을 추출 후 20000미만으로 filter한 값들을 reduce로 add한 값을 반환한다.
위와 결과값은 같다.

const add = (a+b) => a + b;
log(reduce(add,
           filter(n=>n<20000,
               map(p=>p.price, products))));
// 30000

그림 출처

0개의 댓글