배열의 element를 순환할 때 for 문을 사용하는 것보다 메서드를 이용하는 편이 더 콤펙트 하게 작성할 수 있다. 또한 함수를 사용하게 되므로 함수를 사용할 때의 이점을 모두 가져온다.
다음에 나올 예제는 아래의 users 배열을 사용한다.
const users = [
{ name: 'Tim', age: 40 },
{ name: 'Satya', age: 20 },
{ name: 'Sundar', age: 50 }
]
아래 등장하는 메서드는 모두 immutable하다.
for 문 대신에 사용할 수 있는 메서드이다.
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
forEach처럼 element를 하나씩 가져오는 것은 동일하지만, forEach와 달리 element를 가공해서 새로운 배열을 만든다.
function getName(user) {
return user.name;
}
const userNames = users.map(getName);
console.log(userNames); // ["Tim", "Satya", "Sundar"]
users에서 user의 이름만 가져와 새로운 배열을 생성했다. for 문이나 forEach 구현했다면, 새로운 배열에 push하는 형태로 구현했을 테지만, map 메서드에서는 return으로 구현을 해야 한다.
몇 가지 조건을 기준으로 특정 데이터만을 뽑아내고 싶을 경우가 많이 있다. 그럴 때, 사용하기 유용하다.
boolean 타입으로 반환하는 callback 함수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의 작동 원리는 배열 축소다. 배열에서 문자열로, 숫자로, 객체로, 최종적으로 하나의 값으로 만드는 과정이다. 하나의 값으로 만들어주는 함수를 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
| 호출 횟수 | accumulator | currentValue | Return Value |
|---|---|---|---|
| 1번째 호출 | 없음 | 40 | 40 |
| 2번째 호출 | 40 | 20 | 60 |
| 3번째 호출 | 60 | 50 | 110 |
const names = users.reduce(function(acc, user, idx, arr) {
return acc + user.name + ', ';
}, '');
console.log(names.slice(0, names.length - 2)); // 'Tim, Satya, Sundar'
| 호출 횟수 | accumulator | currentValue | Return 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을 잊어버리는 경우가 많은데 빼먹지 않도록 주의해야 한다.
| 호출 횟수 | accumulator | currentValue | Return 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 }]} |