JavaScript 메서드로 데이터 다루기(3. 배열로 함수형 프로그래밍하기)

Park, Jinyong·2020년 4월 9일
1

Code States - Pre Course

목록 보기
5/11

Achievement Goals

  • 메서드를 이용해 문자열과 숫자를 원하는 형태로 만들 수 있다.
  • 메서드를 이용해 배열과 객체를 구분할 수 있다.
  • 객체의 키 및 값의 존재 여부를 확인할 수 있다.
  • 배열의 앞/뒤에 element를 추가 또는 삭제할 수 있다.
  • 불변성의 의미를 이해하고, 어떤 메서드가 Mutable 혹은 Immutable한지 구분할 수 있다.
  • for 문을 대체하여, forEach, filter, map, reduce, slice 등의 메서드를 이용해 배열을 원하는 형태로 만들 수 있다.
  • 불변성을 유지하는 것이 왜 좋은지 이해할 수 있다.
  • 함수형 프로그래밍의 특징을 이해할 수 있다.
    • 함수를 인자로 넘기는 방법에 대해 이해할 수 있다.
    • 메서드의 인자로 익명 함수를 즉시 넘기는 방법에 대해 이해할 수 있다.
    • callback에 대해 이해할 수 있다.

명령형 반복문을 함수형으로 작성하기

배열의 element를 순환할 때 for 문을 사용하는 것보다 메서드를 이용하는 편이 더 콤펙트 하게 작성할 수 있다. 또한 함수를 사용하게 되므로 함수를 사용할 때의 이점을 모두 가져온다.

  • 코드 중복을 줄인다.
  • 모듈성이 향상된다.
  • 복잡한 문제를 해결할 때 도움이 된다.
  • 유지 보수성을 높인다

출처: https://www.freecodecamp.org/news/how-and-why-to-use-functional-programming-in-modern-javascript-fda2df86ad1b/

다음에 나올 예제는 아래의 users 배열을 사용한다.

const users = [
  { name: 'Tim', age: 40 },
  { name: 'Satya', age: 20 },
  { name: 'Sundar', age: 50 }
]

아래 등장하는 메서드는 모두 immutable하다.

forEach

for 문 대신에 사용할 수 있는 메서드이다.

  • arguments: 배열의 element 각각에 대해 실행한 명령을 담은 함수
  • return value: undefined
users.forEach(function(user) {
  console.log(user);
});

// output:
// {name: "Tim", age: 40}
// {name: "Satya", age: 20}
// {name: "Sundar", age: 50}

배열의 element가 함수의 인수로 하나씩 들어가는 모습을 확인할 수 있다. 여기에 함수형 프로그래밍을 적용해 함수 이름을 넣을 수도 있다.

function printUserInfo(user) {
  console.log('Name: ' + user.name + ', ' + 'Age: ' + user.age);
}
users.forEach(printUserInfo);

// output:
// Name: Tim, Age: 40
// Name: Satya, Age: 20
// Name: Sundar, Age: 50

여기서 전달되는 함수를 callback 함수라고 불린다. callback은 다른 함수의 인수로 전달되는 함수를 의미한다. callback 함수가 받을 수 있는 인수로는 배열의 element와 추가로 index 그리고 원본 배열이 있다. 아래 original 매개변수가 원본을 가지고 있다.

function printUserInfo(user, index, original) {
  console.log((index + 1) + '. Name: ' + user.name + ', ' + 'Age: ' + user.age);
}
users.forEach(printUserInfo);

// output:
// 1. Name: Tim, Age: 40
// 2. Name: Satya, Age: 20
// 3. Name: Sundar, Age: 50

map

forEach처럼 element를 하나씩 가져오는 것은 동일하지만, forEach와 달리 element를 가공해서 새로운 배열을 만든다.

  • arguments: element를 가공해서 새로운 element를 반환하는 callback 함수
  • return value: 가공된 새로운 배열
function getName(user) {
  return user.name;
}
const userNames = users.map(getName);
console.log(userNames); // ["Tim", "Satya", "Sundar"]

users에서 user의 이름만 가져와 새로운 배열을 생성했다. for 문이나 forEach 구현했다면, 새로운 배열에 push하는 형태로 구현했을 테지만, map 메서드에서는 return으로 구현을 해야 한다.

filter

몇 가지 조건을 기준으로 특정 데이터만을 뽑아내고 싶을 경우가 많이 있다. 그럴 때, 사용하기 유용하다.

  • arguments: element가 조건에 부합하는지를 boolean 타입으로 반환하는 callback 함수
  • return value: 조건에 의해 걸러진 새로운 배열
function moreThan30(user) {
  return user.age > 30;
}
const above30AgeUsers = users.filter(moreThan30);
console.log(above30AgeUsers); // [{name: "Tim", age: 40}, {name: "Sundar", age: 50}]

callback 함수의 return value가 true false냐에 따라 데이터가 push될지 말지 결정된다.

reduce

reduce의 작동 원리는 배열 축소다. 배열에서 문자열로, 숫자로, 객체로, 최종적으로 하나의 값으로 만드는 과정이다. 하나의 값으로 만들어주는 함수를 reducer라고 부른다.

  • arguments: reducer, (초기값)
  • return value: reducer에 의해 축소된 하나의 값

reducer

reducer의 구성요소설명
누적값 (accumulator)배열의 요소를 하나씩 줄여나가면서 생기는 중간 과정(결과)
현재값 (currentValue)reducer가 받는 배열의 요소
초기값 (initialValue)누적값의 초기 상태

reducer에서 반환된 값은 누적값 accumulator에 누적된다. 초기값이 지정되지 않으면 배열의 첫 번째 값이 초기값이 된다.ㅇ
ㄹㅇㄴㅁㄹㅇㄴㅁ

배열에서 숫자로

const average = users.reduce(function(acc, user, idx, arr) {
  return acc + user.age;
}, 0) / users.length ;
console.log(Math.floor(average)); // 36
호출 횟수accumulatorcurrentValueReturn Value
1번째 호출없음4040
2번째 호출402060
3번째 호출6050110

배열에서 문자열로

const names = users.reduce(function(acc, user, idx, arr) {
  return acc + user.name + ', ';
}, '');
console.log(names.slice(0, names.length - 2)); // 'Tim, Satya, Sundar'
호출 횟수accumulatorcurrentValueReturn Value
1번째 호출'''Tim''Tim, '
2번째 호출'Tim, ''Satya''Tim, Satya, '
3번째 호출'Tim, Satya, ''Sundar''Tim, Satya, Sundar, '

배열에서 객체로

const address = users.reduce(function(acc, user) {
  const initial = user.name[0];
  if (!(initial in acc)) {
    acc[initial] = [];
  }
  acc[initial].push(user);
  return acc;
}, {});
console.log();

return을 잊어버리는 경우가 많은데 빼먹지 않도록 주의해야 한다.

호출 횟수accumulatorcurrentValueReturn Value
1번째 호출{}users[0]{'T': [{ name: 'Tim', age: 40 }]}
2번째 호출{'T': [{ name: 'Tim', age: 40 }]}users[1]{'T': [{ name: 'Tim', age: 40 }, 'S': [{ name: 'Satya', age: 20 }]}
3번째 호출{ name: 'Tim', age: 40 }, 'S': [{ name: 'Satya', age: 20 }]}users[2]{ name: 'Tim', age: 40 }, 'S': [{ name: 'Satya', age: 20 }, { name: 'Sundar', age: 50 }]}

0개의 댓글