배열의 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 }]} |