본문 바로가기
JavaScript

[JavaScript] 함수형으로 전환하기 filter, map, each

by WaterPunch 2021. 9. 22.

함수형 프로그래밍을 배우기 전에는 

 

우리는 아래와 같은 코드를 사용했을 것입니다

 

(결과값도 포함해서 작성하니 조금 더러워보여도 양해 바랍니다)

var users = [
  { id: 1, name: 'ID', age: 36 },
  { id: 2, name: 'BJ', age: 32 },
  { id: 3, name: 'JM', age: 32 },
  { id: 4, name: 'PJ', age: 27 },
  { id: 5, name: 'HA', age: 25 },
  { id: 6, name: 'JE', age: 26 },
  { id: 7, name: 'JI', age: 31 },
  { id: 8, name: 'MP', age: 23 }
];

========================================================
	
// 1. 명령형 코드 (기존 방식)
// 1-1. 30세 이상인 users를 거른다
var temp_users = [];
for(var i=0; i<users.length; i++) {
  if(users[i].age >=30) {
    temp_users.push(users[i]);
  }
}
console.log(temp_users);
/*
(4) [{…}, {…}, {…}, {…}]
  0: {id: 1, name: 'ID', age: 36}
  1: {id: 2, name: 'BJ', age: 32}
  2: {id: 3, name: 'JM', age: 32}
  3: {id: 7, name: 'JI', age: 31}
  length: 4
  [[Prototype]]: Array(0)
*/


// 1-2 30세 이상인 user의 names를 수집한다
var names= [];
for(var i=0; i<temp_users.length; i++){
  names.push(temp_users[i].name);
}
console.log(names);
// (4) ['ID', 'BJ', 'JM', 'JI']

========================================================

//1-3. 30세 미만인 users를 거른다
var temp_users = [];
for(var i=0; i<users.length; i++){
  if(users[i].age < 30) {
    temp_users.push(users[i]);
  }
}
console.log(temp_users);
/*
(4) [{…}, {…}, {…}, {…}]
  0: {id: 4, name: 'PJ', age: 27}
  1: {id: 5, name: 'HA', age: 25}
  2: {id: 6, name: 'JE', age: 26}
  3: {id: 8, name: 'MP', age: 23}
  length: 4
  [[Prototype]]: Array(0)
*/


// 1-4 30세 미만인 user의 names를 수집한다
var names = [];
for(var i=0; i<temp_users.length; i++){
  names.push(temp_users[i].name);
}
console.log(names);
// (4) ['PJ', 'HA', 'JE', 'MP']

 

위의 코드를 보면 많은 코드가 중복이 된다

 

이것을 함수형으로 바꿔본다

 

var users = [
  { id: 1, name: 'ID', age: 36 },
  { id: 2, name: 'BJ', age: 32 },
  { id: 3, name: 'JM', age: 32 },
  { id: 4, name: 'PJ', age: 27 },
  { id: 5, name: 'HA', age: 25 },
  { id: 6, name: 'JE', age: 26 },
  { id: 7, name: 'JI', age: 31 },
  { id: 8, name: 'MP', age: 23 }
];

//_filter로 리팩토링(순수함수로 만듬 > 조건을 외부로 빼준다)

//배열을 조건문을 통해 거른다
function _filter(list, predi){
  var new_list = [];                   // 결과값을 넣을 new_list 배열 생성
  for(var i=0; i<list.length; i++){
    if(predi(list[i])){
      new_list.push(list[i]);          // predi(조건)에 맞는 user를 new_list 배열에 push
    }
  }
  return new_list;
}

console.log(_filter([1,2,3,4], function(num){ return num % 2; }));      //(2) [1, 3]
console.log(_filter([1,2,3,4], function(num){ return !(num % 2); }));   //(2) [2, 4]

// 30세 이상의 user list
console.log(_filter(users, function(user) {return user.age>=30; }));
var over_30 = _filter(users, function(user) {return user.age>=30; });
console.log(over_30);
/* 방식만 조금 다르고 결과는 같다
(4) [{…}, {…}, {…}, {…}]
  0: {id: 1, name: 'ID', age: 36}
  1: {id: 2, name: 'BJ', age: 32}
  2: {id: 3, name: 'JM', age: 32}
  3: {id: 7, name: 'JI', age: 31}
  length: 4
  [[Prototype]]: Array(0)
*/

// 30세 미만의 user list
console.log(_filter(users, function(user) {return user.age<30; }));
var under_30 = _filter(users, function(user) {return user.age<30; });
console.log(under_30);
/* 방식만 조금 다르고 결과는 같다
(4) [{…}, {…}, {…}, {…}]
  0: {id: 4, name: 'PJ', age: 27}
  1: {id: 5, name: 'HA', age: 25}
  2: {id: 6, name: 'JE', age: 26}
  3: {id: 8, name: 'MP', age: 23}
  length: 4
  [[Prototype]]: Array(0)
*/

 

이해가 안된다면 밑에 이미지 참고... (너무 개떡같이 그려놓은거 같음)

① _filter(users, function(user) { return user.age>=30; }) 실행

② users 는 왼쪽의 배열의 값을 가져온다

③ function _filter(list, predi) 의

    list에는 users 가 들어가고, predi에는 function(user) { return user.age>=30; }가 들어감

④ predi에 맞는 list들이 for문을 돌면서 뽑히면 new_list라는 새로운 배열에 넣어줘서 return 한다

⑤ return 된 결과값이 console.log를 통해 출력된다

 

여기서 중요한건 predi이다

users가 어떠한 것이 들어오던지 if 문의 조건은 predi 함수에 위임한다

따라서 _filter 함수는 거르는 그 자체만 하게 되고 return 으로 predi 처리된 new_list배열을 전달한다

 

이로써 다형성과 재사용성이 매우 높아졌다. 

 

 

다음으로 _map 함수를 사용해보자

function _map(list, mapper) {
  var new_list = [];
  for(var i=0; i<list.length; i++){
    new_list.push(mapper(list[i]));
  }
  return new_list;
}

// 위에서 만든 over_30 객체를 사용한다
var over_30_names = _map(over_30, function(user){
	return user.name;
})
console.log(over_30_names); // (4) ['ID', 'BJ', 'JM', 'JI']


var over_30_ages = _map(over_30, function(user){
	return user.age;
})
console.log(over_30_ages); // (4) [36, 32, 32, 31]

생김새는 _filter와 비슷하게 생겼다

 

만약 over_30 대신에 users를 넣는다면 모든 user들의 name과 age가 뽑혀 결과값으로 나올 것이다

 

 

함수형 프로그래밍은 값을 만들어놓고 문장을 내려가면서 변형해 나가는게 아니라
함수를 통과해 나가면서 한번에 값을 새롭게 만들어나가는 과정으로 진행된다

 

 

함수형 프로그래밍을 극대화 시키기 위해서 _filter와 _map을 동시에 사용해보자

console.log(
  _map(
  _filter(users, function(user) { return user.age >= 30; }),
  function(user) { return user.name; }
  )
);
// (4) ['ID', 'BJ', 'JM', 'JI']

이렇게 사용할 수 있다

 

하지만 _filter와 _map 함수에 for문이 중복이 된다

 

 

이것을 _each에 for문의 역할을 위임해서 _filter와 _map의 for문 중복을 줄여보자

 

var users = [
  { id: 1, name: 'ID', age: 36 },
  { id: 2, name: 'BJ', age: 32 },
  { id: 3, name: 'JM', age: 32 },
  { id: 4, name: 'PJ', age: 27 },
  { id: 5, name: 'HA', age: 25 },
  { id: 6, name: 'JE', age: 26 },
  { id: 7, name: 'JI', age: 31 },
  { id: 8, name: 'MP', age: 23 }
];

function _each(list, iter) {
  for(var i=0; i<list.length; i++){
    iter(list[i]);
  }
  return list;
}

function _filter(list, predi){
  var new_list = [];
  _each(list, function(val){
    if(predi(val)){
      new_list.push(val);
    }
  });
  return new_list;
}

function _map(list, mapper) {
  var new_list = [];
  _each(list, function(val, key) {
    new_list.push(mapper(val, key));
  });
  return new_list;
}

이렇게 _filter와 _map에 for문을 줄여주어 재사용성을 높여주었다

 

 


아래의 코드를 예시로 동작과정을 설명해보겠다

console.log(
  _map(
  _filter(users, function(user) { return user.age >= 30; }),
  function(user) { return user.name; }
  )
);
// (4) ['ID', 'BJ', 'JM', 'JI']

 

 

① 우선 _map 내부에 있는 _filter 함수부터 실행이 된다

② _each는 users와 user.age>=30 을 받아서 for문을 돌려 age가 30 이상인 list를 return 한다

③ return한 list를 new_list에 push(배열에 넣음)하고 _filter함수는 new_list를 return하게 된다 

(4) [{…}, {…}, {…}, {…}]
  0: {id: 1, name: 'ID', age: 36}
  1: {id: 2, name: 'BJ', age: 32}
  2: {id: 3, name: 'JM', age: 32}
  3: {id: 7, name: 'JI', age: 31}
  length: 4
  [[Prototype]]: Array(0)

(_filter함수 return값은 age가 30 이상인 list(배열)) 

 

_map(age가 30 이상인 list, function(user) { return user.name; })

    이렇게 되고, 위와 비슷한 방식으로 _map함수가 실행되면서 age가 30 이상인 list에서 name만 뽑혀 나오게된다

⑤ 마지막으로 console.log로 결과 출력 뙇

(4) ['ID', 'BJ', 'JM', 'JI']

이렇게 나오게 된다

반응형

댓글