널리 사용되는 Airbnb 자바스크립트 컨벤션을 보면, for-in과 for-of와 같은 루프 대신 map, forEach, reduce과 같은 자바스크립트의 함수를 사용하라고 권장하고 있다.
(하지만 Airbnb 자바스크립트 컨벤션이 '클린코드'인가에 대해서는 이견이 있다. 2. forEach
부분에서 자세히 설명함)
현재 진행 중인 우테코 프리코스에서도 for-in과 for-of 대신 자바스크립트의 내장 함수를 쓰라는 리뷰를 많이 받았어서, 이번 포스팅에서는 map, forEach, reduce 함수에 대하여 알아보려고 한다.
사실 이전에 [자바스크립트] 배열 내장함수에 대해 알아보자 포스팅을 통해 자바스크립트 내장 함수에 대해 공부한 적이 있다. 하지만 거의 이론 위주만 알아보고 실제로 코딩을 해보지는 않아서 이 함수들을 실제로 쓰지 못했던 것 같다.
그래서 이번 포스팅에서는 실제로 map, forEach, reduce를 여러 예시를 들어가며 직접 코딩하며 익혀보려고 한다.
arr.map(callback(currentValue[, index[, array]])[, thisArg])
배열.map((요소, 인덱스, 배열) => { return 요소 })
const arr = [10, 20, 30];
//일반 함수 형태
arr.map(function(item, index) {
console.log(index+"번 값", item);
});
//화살표 함수 형태
arr.map((item, index) => {
console.log(index+"번 값", item);
});
// "0번 값"
// 10
// "1번 값"
// 20
// "2번 값"
// 30
// "3번 값"
// 40
const arr = [1, 2, 3, 4, 5];
const result = arr.map((e) => {
return e;
})
console.log(result); // [1, 2, 3, 4, 5]
const arr = [1, 2, 3, 4, 5];
const result = arr.map(e => e)
console.log(result); // [1, 2, 3, 4, 5]
const arr = [1, 2, 3, 4, 5];
const result = arr.map((e) => {
return e + 1;
})
console.log(result); // [2, 3, 4, 5, 6]
const result = arr.map(e => e + 1)
console.log(result); // [2, 3, 4, 5, 6]
const arr = [1, 2, 3, 4, 5];
const result = arr.map(e => e * e)
console.log(result); // [1, 4, 9, 16, 25]
map 안에는 함수가 오기만 하면 된다.(Math.sqrt)
const arr = [1, 2, 3, 4, 5];
const squares1 = arr.map(Math.sqrt);
const squares2 = arr.map(e => Math.sqrt(e));
console.log(squares1); // [1, 1.4142135623730951, 1.7320508075688772, 2, 2.23606797749979]
console.log(squares2); // [1, 1.4142135623730951, 1.7320508075688772, 2, 2.23606797749979]
const users = [
{ name: 'YD', age: 22 },
{ name: 'Bill', age: 32 },
{ name: 'Andy', age: 21 },
{ name: 'Roky', age: 35 },
];
const ages = users.map(user => user.age);
console.log(ages); // [22, 32, 21, 35]
const users = [
{ name: 'YD', age: 22 },
{ name: 'Bill', age: 32 },
{ name: 'Andy', age: 21 },
{ name: 'Roky', age: 35 },
];
const newUsers = users.map(user => {
if (user.name === 'YD') {
return { ...user, age: 18 }; // ...user가 없으면 name이 들어가지 않음
}
return { ...user }; // 이게 없으면 name이 'YD'인 부분만 정의되고 나머지는 undefined가 됨
})
console.log(newUsers);
// [[object Object] {
// age: 18,
// name: "YD"
// }, [object Object] {
// age: 32,
// name: "Bill"
// }, [object Object] {
// age: 21,
// name: "Andy"
// }, [object Object] {
// age: 35,
// name: "Roky"
// }]
arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])
배열.forEach((요소, 인덱스, 배열) => { return 요소 })
forEach를 사용할 때는 함수를 따로 변수에 할당하지 않는다.
const students = ['John', 'Sara', 'Jack'];
students.forEach(e => {
console.log(e);
});
// John
// Sara
// Jack
map으로 각 원소를 출력했을 때와 비교해보자.
const arr = [1, 2, 3, 4, 5];
const result = arr.map((e) => {
return e;
})
console.log(result); // [1, 2, 3, 4, 5]
우테코 프리코스를 하며 1주 차 미션을 제출하고 받은 코드 리뷰에서 forEach가 언급된 적이 있다. for 대신 forEach를 써보라는 조언이었다.
지적받은 내 코드는 아래와 같다.
const nameList = ['제이엠', '이엠지', '제제이', '엠지엠', '엘렐레'];
const twoLetterList = ['제이', '엠지', '지엠'];
const countList = [0, 0, 0];
for (let i of nameList) {
for (let j = 0; j < twoLetterList.length; j++) {
let k = twoLetterList[j];
if (i.indexOf(k) !== -1) {
countList[j] = countList[j] + 1;
}
}
}
console.log(countList);
리뷰를 해주신 민재 님의 코드를 한번 살펴보았다. forEach로 각 단어를 탐색하며 순회하고 있다.
function checkDuplicatePattern(wordSet, patterns, emailSet, email) {
wordSet.forEach((word) => {
if(!patterns[word]) {
patterns[word] = email;
return;
}
});
}
forms.forEach(([email, nickName]) => {
const wordSet = makeWordSet(nickName);
checkDuplicatePattern(wordSet, patterns, emailSet, email);
});
나도 forEach로 코드를 바꿔보기로 했다. 바꾸면 아래와 같다. 이게.. 그냥 for문을 쓰는 것보다 좋다는 거겠지?
const nameList = ['제이엠', '이엠지', '제제이', '엠지엠', '엘렐레'];
const twoLetterList = ['제이', '엠지', '지엠'];
let countList = [0, 0, 0];
nameList.forEach((name) => {
twoLetterList.forEach((twoLetter, index) => {
if (name.indexOf(twoLetter) !== -1) {
console.log(index+'번째에'+twoLetter+'가 겹칩니다');
countList[index] = countList[index] + 1;
}
})
})
console.log(countList);
forEach로 바꿨을 때 어떤 점이 좋은 건지 모르겠어서 개발자 커뮤니티에 질문을 올려보았다.
네 분이 답변해주셨고, 예상 외의 답변들이 와서 조금 당황했는데.. 정리해보면
<그렇게 바꿔서 좋을 거 없다는 것으로 의견이 존재함>이었다.
자세한 답변은 아래와 같다.
- for을 forEach 등으로 쓰는 것에 대하여 좋은 거 없고 for of를 더 권장하는 의견이 많을 것이다.
- 하지만 인덱스까지 같이 필요한 경우에는 forEach가 더 편하긴 함. 단순 순회는 for...of가 낫고 인덱스 필요하면 forEach를 써서 두 번째 인자를 받는 것이 낫다.
- map, filter는 성능이 정말 중요한 경우가 아니면 클린 코드가 맞고, reduce는 실질적으로 가독성이 좋아지는지는 약간 논란의 여지가 있지만 맞다고 치지만, forEach는 취향이 맞다.
- for 대신 forEach랑 reduce가 낫다는 주장에 전혀 동의하지 않는다.
- Airbnb 자바스크립트 컨벤션은
"클린코드" (X)
"한 회사의 전통" (O)- 신규 프로젝트에서 에어비엔비 룰 사용을 엄격히 금지해야 한다.
- forEach를 사용하면 어떤 배열을 순회하는지 바로 알 수 있어서 좋은 것 같다
Airbnb 자바스크립트 컨벤션이 완전히 옳은 클린코드인줄 알았는데, 이렇게 부정적인 의견이 있어서 놀랐다. 우테코에서 Airbnb 컨벤션을 지키라고 하긴 하지만, 관련해서 부정적인 의견이 있다는 것도 알고가야겠다!
arr.reduce(callback[, initialValue])
배열.reduce((누산기, 현재요소, 인덱스, 배열) => { return 요소 })
const arr = [1, 2, 3, 4, 5];
function callbackFunc(accumulator, currentValue, Index, array) {
console.log("acc: " + accumulator + ", cur: " + currentValue
+ ", curIndex: " + Index + ", array: " + array);
return accumulator + currentValue;
}
const result = arr.reduce(callbackFunc, 0);
console.log(result);
// "acc: 0, cur: 1, curIndex: 0, array: 1,2,3,4,5"
// "acc: 1, cur: 2, curIndex: 1, array: 1,2,3,4,5"
// "acc: 3, cur: 3, curIndex: 2, array: 1,2,3,4,5"
// "acc: 6, cur: 4, curIndex: 3, array: 1,2,3,4,5"
// "acc: 10, cur: 5, curIndex: 4, array: 1,2,3,4,5"
// 15
const numbers = [1, 2, 3, 4, 5];
const sumNumber = numbers.reduce((accumulator, currentNumber) => accumulator + currentNumber, 0);
console.log(sumNumber); // 15
map, forEach, reduce를 재미있게 정리한 것이 있어 가져와봤다.
map([🌽, 🐮, 🐔], cook)
=> [🍿, 🍔, 🍳]
forEach([🍿, 🍔], eat)
=> undefined
reduce([🍿, 🍳], eat)
=> 💩
실제로 '1, 2, 3, 4, 5, 6'
을 [1, 2, 3, 4, 5, 6]
으로 바꿔야 할 일이 있어서 처음에는 forEach를 사용하려고 했다. 하지만 forEach는 undefined를 반환하기 때문에 의도대로 값이 나오지 않았고, map을 써서
'1, 2, 3, 4, 5, 6'.split(',').map(number => Number(number));
라고 쓰는게 맞았다.
map과 forEach가 비슷한 줄 알았는데, 전혀 다르다는 걸 알 수 있었다.
이번 포스팅에서는 자바스크립트의 내장 함수 map, forEach, reduce에 대해 알아보았다.
가장 유명한 Airbnb 자바스크립트 컨벤션에서는 for-of, for-in 대신 forEach, reduce, map 등을 사용하라고 권장하고 있다. 그리고 우테코에서는 Airbnb 자바스크립트 컨벤션을 지키라고 하고 있어서 코드 리뷰 스터디 팀원들은 이를 참고하여 내 코드에 for 대신 forEach를 사용하라는 리뷰를 남겼다.
그래서 이 포스팅을 시작한 건데..! for 대신 forEach가 낫지 않다는 의견들이 있어서 놀랐다(인덱스가 필요한 때를 빼고). 하지만 항상 궁금했던 자바스크립트의 내장 함수에 대해 한번 더 짚고 넘어갈 수 있어서 좋은 경험이었다!
헉..! forEach에 관해서 다시 한 번 생각하게 되네요.. 좋은 글 감사합니다!