본문 바로가기

카테고리 없음

함수형 자바스크립트 (3) - map, filter, reduce

함수형 프로그래밍에서 자료구조를 순차적으로 탐색 및 변환하는데 쓰이는 실용적인 연산들에 대해 알아보도록 하겠습니다. 이들은 주로 수동 루프를 없애기 위해 사용됩니다.

 

map

map은 데이터 컬렉션의 원소를 모두 변환해야 할 때 사용합니다. 예를 들어 학생 객체를 원소를 가지는 students라는 배열이 있고 학생들의 성(last name)만 뽑은 배열을 만들고 싶을 때, map을 사용할 수 있습니다.

const students = [ { name: 'Michael Jackson'}, {name: 'Michael Jordan'}, {name: 'David Beckham'}]

const studentLastNames = students.map(student => student.name.split(' ')[1])

console.log(studentLastNames) // ['Jackson', 'Jordan', 'Beckham']

map은 고계함수로, 각 원소에 적용할 함수를 인자로 받습니다. 이 예제에서는 인자로 받은 학생의 이름을 공백을 기준으로 분리하여 배열을 생성하고, 그 중에 두번째 값을 반환하는 화살표 함수를 map의 인자로 넘겼습니다. map 함수의 결과값은 각 원소에 적용한 함수의 리턴값의 배열입니다.

 

reduce

reduce는 데이터 컬렉션의 원소들을 이용하 의미 있는 하나의 결과를 도출할때 사용합니다. map과 다른 점은 배열을 반환한는 것이 아닌 하나의 값을 반환한다는 것입니다. 예를 들어 배열의 원소를 모두 더한 값을 얻고 싶을 때 사용할 수 있습니다.

const numbers = [1,2,3,4,5,6,7,8]
const result = numbers.reduce((accumulated, number) => {
  accumulated += number
}, 0)

console.log(result) // 36

reduce도 마찬가지로 각 원소에 적용할 함수를 인자로 받습니다. 그러나 map과 다른 점은 인자로 받는 함수의 인자들인데요. reduce가 인자로 받는 함수(reducer)의 첫 번째 인자는 축적된 값을 저장하는 변수인 accumulated입니다. 즉 매 원소를 돌면서 이때까지 누적된 값을 첫번째 인자로 받고, 두번째 인자로 해당 원소를 받게 됩니다. 또한 reduce의 두번째 인자는 accumulated의 기본 값입니다. 해당 코드는 배열의 모든 수를 더하는 것입니다. 따라서 모든 값이 더해진 누적치의 시작값을 0으로 해놓았습니다. 만약 모든 수를 곱하는 코드를 작성하고자 했다면 기본값을 1로 해놨을 것입니다. 

 

Map, reduce를 사용한다면 코드를 더 단순화할 수 있습니다. 예를 들어 학생들의 수학 점수의 평균을 얻는 코드를 구하고자 할때 다음과 같이 작성할 수 있습니다.

 

/** student 객체는 다음의 구조를 가진다고 예정

student = {
  name: String,
  scores: [
    english: Number,
    math: Number
  ]
}

**/

// 학생의 수학 점수를 반환하는 함수
const getMathScore = student => student.score.math

// 두 개의 수를 더한 값을 반환하는 함수
const add = (num1, num2) => num1 + num2

// 학생들의 배열을 수학 점수 배열로 변환하고, 모든 점수를 더하는 함수
const getTotalScore = students => students.map(getMathScore).reduce(add, 0)

// 총 수학 점수를 총 학생 수로 나누어 평균값을 구하는 함수
const getAverageMathScore = students = > getTotalScore(students)/students.length

이전과는 다르게 함수를 외부에 변수에 선언하여 이를 map, reduce의 인자로 넘겨서 작성을 했습니다. 이 덕분에 코드의 가독성과 재사용성이 증가한 것을 볼 수 있습니다. 또한 화살표 함수덕분에 코드가 매우 간결해졌습니다.

filter

filter는 배열에서 특정 조건으로 원하지 않는 원소를 제거하기 위해 사용합니다. 예를 들어 학생들중에서 남학생들의 배열을 뽑고자한다면 다음과 같이 사용할 수 있습니다.

const maleStudents = students.filter(student => student.gender === 'male')

filter도 마찬가지로 함수를 인자를 받습니다. 중요한건 이 함수의 반환값인데요, 해당 함수의 반환값이 true인 경우(정확히 말하자면 truthy한 경우)의 원소만을 새로운 배열의 원소로 추가합니다.

 

마무리

이번에는 함수형 프로그래밍에서 중요한 3가지 배열 메서드에 대해 알아봤습니다. 이 3개의 메서드는 자바스크립트 배열의 메서드로 구현되어 있으며, underscore.js나 lodash 라이브러리에도 구현되어 있어서 가져다가 사용하면 됩니다.  이 메서드들을 사용하면 재사용성, 가독성이 증가하고, 수동적으로 배열을 순회할 필요가 없어 혹시 모를 버그가 발생할 가능성도 줄어듭니다.

 

추가적으로, 자주 쓰는 배열의 메서드 중 하나인 forEach는 함수형 프로그래밍에 적합하지 않습니다. 이는 forEach는 배열을 순회할 뿐, 새로운 값을 반환하지 않기 때문입니다. 즉 forEach를 사용하여 배열의 원소를 변경하게 된다면 불변성을 지키지 않게 됩니다.

 

정리

  • map은 배열의 각 원소를 이용해 똑같은 길이의 새로운 배열을 생성할 때 사용합니다.
  • reduce는 배열의 원소들을 이용해 하나의 결과값을 도출하고자 할 때 사용합니다.
  • filter는 배열에서 특정 조건으로 원소를 제거한 새로운 배열을 생성할 때 사용합니다.