관련 지식
#javascript #filter #map #reduce

자바스크립트에는 Array 객체에는 filter()map()reduce() 함수가 있습니다. 그런데 잘 안쓰는 분들이 많습니다. 그러한 함수가 있다는걸 몰라서, 그냥 for 구문을 쓰면 되니까 또는 언제 써야할지 몰라서 안쓰게 되죠.

해당 함수들에 대해 설명한 많은 블로그에서 대부분 덧셈, 곱셈으로 설명하고 있어서 저는 조금 다른 예제를 보여드리고자 합니다.

filter() 함수

아래와 같은 데이터가 있다고 가정합니다.

  1. var arr = [
  2. { name : '삼겹살', price : 13000},
  3. { name : '족발', price : 33000},
  4. { name : '치킨', price : 18000},
  5. { name : '광어회', price : 20000},
  6. { name : '보쌈', price : 34000},
  7. { name : '짜장면', price : 7000}
  8. ];

filter는 매우 직관적인 이름입니다. 위 데이터에서 특정 조건에 맞는 데이터만 골라내고 싶을때 filter() 함수를 사용하면 됩니다.

  1. //2만원 이상의 메뉴만 골라내기
  2. var arr1 = arr.filter(function(menu) {
  3. return menu.price >= 20000;
  4. });
  5. console.log(arr1);

2만원 이상의 메뉴만 가진 배열 arr1이 생성됩니다. 즉 return 되는 조건이 true인 항목만 필터링 되는 것입니다. 물론 원본 배열인 arr 변수의 값에는 변화가 없습니다.

map() 함수

map() 함수는 filter() 함수와 동일한 인자를 가집니다. 위 소스에서 함수 이름만 ‘map’ 으로 바꿔서 만든 소스를 실행해 보죠.

  1. var arr2 = arr.map(function(menu) {
  2. return menu.price >= 20000;
  3. });
  4. console.log(arr2); //[false, true, false, true, true, false] 출력

filter() 함수와는 달리 원본 배열과 동일한 갯수를 가진 변수 arr2가 만들어졌습니다. 변수의 값을 보면 가격이 2만원 이상인 메뉴 위치가 true로, 나머지는 false 값이 되었음을 알 수 있습니다. 즉 map() 함수에선 return 결과가 곧 배열 값이 됩니다. return 항목을 다른값으로 바꿔 보겠습니다.

  1. var arr3 = arr.map(function(menu) {
  2. var obj = {
  3. name : menu.name,
  4. label : ''
  5. };
  6. if(menu.price >= 20000)
  7. obj.label = '얻어먹고 싶음';
  8. return obj;
  9. });
  10. console.log(arr3);

배열 arr 과는 다른 데이터 포맷을 가진 arr3이 만들어졌습니다.

filter() 와 map() 의 차이점

여기서 filter와 map 함수의 가장 중요한 특징을 캐치해야 합니다.

  • filter 함수는 원본 배열과 크기가 다른 배열을 만들수 있음(데이터 포맷은 고정)
  • map 함수는 원본 배열과 다른 포맷의 배열을 만들수 있음(배열 크기는 고정)

만약 조건에 따라 배열 크기와 데이터 포맷이 다른 배열을 만들려면 어떻게 할까요?
reduce 함수를 쓰면 됩니다.

reduce() 함수

  1. var arr4 = arr.reduce(function(accumulator, menu) {
  2. if(menu.price >= 20000)
  3. accumulator.push(menu.name);
  4. return accumulator;
  5. }, ['abc']);
  6. console.log(arr4); //["abc", "족발", "광어회", "보쌈"] 출력

배열크기도 다르고 데이터 포맷도 다른 배열이 만들어졌습니다. reduce() 함수는 조금 다른 인자를 가지고 있습니다. 콜백함수 이외에 초기값을 설정할 수 있는데(여기서는 ['abc'] 가 초기값) 초기값이 있을때와 없을때 동작 방식이 조금 달라지므로 주의가 필요합니다. => 매개변수 항목 참고

어떤 사람은 아래와 같이 만들면 되지 않겠냐고 말할수 있습니다.

  1. var arr5 = ['abc'];
  2. for(var i = 0; i < arr.length; i++) {
  3. var menu = arr[i];
  4. if(menu.price >= 20000)
  5. arr5.push(menu.name);
  6. }
  7. console.log(arr5);

reduce() 함수를 이용한 코딩과 몇줄 차이나지도 않죠. 그러나 위 소스는 reduce() 함수와는 달리 안 좋은 점이 있습니다. 바로 변수와 로직에 결합도가 높다는 것이죠. 만약 arr5 변수의 이름을 바꾸고 싶으면 for 구문 내에서도 이름을 바꿔야겠죠? 결합도가 높은 것입니다.

arr4 변수를 만든것과 같은 케이스의 코딩을 하려면 아래와 같이 해야합니다. 복잡한가요? 그냥 reduce() 함수를 사용하시면 편합니다.

  1. var arr6 = (function(tmp) {
  2. for(var i = 0; i < arr.length; i++) {
  3. var menu = arr[i];
  4. if(menu.price >= 20000)
  5. tmp.push(menu.name);
  6. }
  7. return tmp;
  8. })(['abc']);
  9. console.log(arr6);

정리

  • 원본 배열의 데이터 포맷은 유지하면서 조건에 맞는 데이터만 골라낼때
    -> filter() 함수
  • 원본 배열 전체에 대해서 조건에 따라 데이터를 가공하고 싶을때
    -> map() 함수
  • 원본 배열과 데이터 포맷도 다르고 데이터를 가공하고 싶을때
    -> reduce() 함수

인터넷 익스플로러 8도 지원해야 하기 때문에 사용할 수 없다구요? 그렇기 때문에 pollyfill 이 있습니다. 다음번엔 pollyfill에 대해서 다뤄보겠습니다.

최종 샘플

  1. var arr = [
  2. { name : '삼겹살', price : 13000},
  3. { name : '족발', price : 33000},
  4. { name : '치킨', price : 18000},
  5. { name : '광어회', price : 20000},
  6. { name : '보쌈', price : 34000},
  7. { name : '짜장면', price : 7000}
  8. ];
  9. var arr1 = arr.filter(function(menu) {
  10. return menu.price >= 20000;
  11. });
  12. console.log(arr1);
  13. var arr2 = arr.map(function(menu) {
  14. return menu.price >= 20000;
  15. });
  16. console.log(arr2); //[false, true, false, true, true, false] 출력
  17. var arr3 = arr.map(function(menu) {
  18. var obj = {
  19. name : menu.name,
  20. label : ''
  21. };
  22. if(menu.price >= 20000)
  23. obj.label = '얻어먹고 싶음';
  24. return obj;
  25. });
  26. console.log(arr3);
  27. var arr4 = arr.reduce(function(accumulator, menu) {
  28. if(menu.price >= 20000)
  29. accumulator.push(menu.name);
  30. return accumulator;
  31. }, ['abc']);
  32. console.log(arr4); //["족발", "광어회", "보쌈"] 출력
  33. var arr5 = ['abc'];
  34. for(var i = 0; i < arr.length; i++) {
  35. var menu = arr[i];
  36. if(menu.price >= 20000)
  37. arr5.push(menu.name);
  38. }
  39. console.log(arr5);
  40. var arr6 = (function(tmp) {
  41. for(var i = 0; i < arr.length; i++) {
  42. var menu = arr[i];
  43. if(menu.price >= 20000)
  44. tmp.push(menu.name);
  45. }
  46. return tmp;
  47. })(['abc']);
  48. console.log(arr6);