개발 공부/JavaScript 공부

[JavaScript] 함수형 프로그래밍(2)

종범2 2020. 7. 5. 17:19

이전 글에서 설명한 내용을 토대로 함수형 프로그래밍으로 작성한 forEach, filter, map, reduce 함수를 설명하겠다. 세 함수 모두 javascript를 사용한다면 익숙한 함수이다. 이 함수들은 공통적으로 배열의 메서드로 존재한다. 함수형 프로그래밍에서는 특정 객체의 메서드가 아니라 함수 자체로 선언하고 사용하므로 다음과 같이 작성한다.

forEach

forEach 함수는 배열과 유사한 args를 받아 func에 인자로 전달하고 args를 반환하는 함수이다.

function forEach(args, func) {
  let keyList = keys(args);
  for (let i = 0; i < keyList.length; i++) {
    func(args[keyList[i]]);
  }
  return args;
}

forEach 내부에서는 keys 함수를 사용하기 때문에 인자로 null이나 undefined를 받더라도 에러가 발생하지 않는다. 또한 배열뿐만아니라 객체를 전달하더라도 각 속성에 따른 값을 func에 전달할 수 있다.

const userList = [
  { age: 10, name: '홍길동' },
  { age: 20, name: '김민수' },
  { age: 30, name: '이민지' }
]

forEach(userList, user => console.log(user.age)); // 10 20 30
forEach(null, user => console.log(user.age)); // 출력값 없음

filter

filter 함수는 배열과 유사한 args를 받아 func에 인자를 전달하고 true가 나오는 경우만 새로운 배열에 넣어 배열을 반환하는 함수이다.

function filter(args, func) {
  const newList = [];
  forEach(args, arg => {
    if (func(arg)) {
      newList.push(arg);
    }
  })
  return newList;
}

filter 내부에서는 forEach를 사용하기 때문에 유사하게 인자로 null이나 undefined를 받더라도 에러가 발생하지 않는다. 또한 배열뿐만아니라 객체를 전달하더라도 각 속성에 따른 값을 func에 전달할 수 있다.

const userList = [
  { age: 10, name: '홍길동' },
  { age: 20, name: '김민수' },
  { age: 30, name: '이민지' }
]

let userOver10 = filter(userList, user => user.age > 10);

console.log('filter:', userOver10); // [ { age: 20, name: '김민수' }, { age: 30, name: '이민지' } ]

map

map 함수는 배열과 유사한 args를 받아 func에 인자로 전달하여 반환된 값을 새로운 배열에 넣어 반환하는 함수이다.

function map(args, func) {
  const newList = [];
  forEach(args, arg => {
    newList.push(func(arg));
  })
  return newList;
}

마찬가지로 forEach를 사용하기 때문 null이나 undefined를 받더라도 에러가 발생하지 않는다. 또한 배열뿐만아니라 객체를 전달하더라도 각 속성에 따른 값을 func에 전달할 수 있다.

const userList = [
  { age: 10, name: '홍길동' },
  { age: 20, name: '김민수' },
  { age: 30, name: '이민지' }
]

let userName = map(userList, user => user.name);

console.log('map:', userName); // [ '홍길동', '김민수', '이민지' ]

reduce

reduce 함수는 배열과 유사한 args를 받아 반복적으로 func에 memo와 요소를 인자로 전달하여 반환된 값을 memo에 저장하고 memo를 반환하는 함수이다.

const forEach = require('./forEach')
function reduce(args, func, memo) {
  if (arguments.length === 2) {
    memo = args[0];
    args = Array.prototype.slice.call(args, 1)
  }
  forEach(args, (val) => {
    memo = func(memo, val);
  })
  return memo;
}

reduce 함수를 map과 get 함수를 이용하여 userList에서 유저의 나이를 모두 더한 값을 반환하는 예제를 작성하였다.

const userList = [
  { age: 10, name: '홍길동' },
  { age: 20, name: '김민수' },
  { age: 30, name: '이민지' }
]

let totalAge = reduce(map(userList, get('age')), (a, b) => a + b)

console.log('reduce + map + get: ', totalAge); // 60

다른 예졔를 생각해보자. 예를 들어 유저 배열중에 나이가 10 초과인 유저들의 이름을 가져오고 싶다면 다음과 같이 코드를 작성한다.

const userList = [
  { age: 10, name: '홍길동' },
  { age: 20, name: '김민수' },
  { age: 30, name: '이민지' }
]

const userNameOver10 = map(
  filter(userList, user => user.age > 10),
  get('name'));
  
console.log('map + filter + get:', userNameOver10); // [ '김민수', '이민지' ]

아직까지는 코드의 가독성이 그렇게 많이 올라갔을까라는 생각이 든다. 다음 글에서는 go와 pipe 함수를 설명하고 두 함수를 사용했을 때 얼마나 가독성이 더 올라가는지 설명하겠다.