본문 바로가기
Javascript

javascript map(), filter(), find(), reduce()

by @hohoya33 2013년 11월 21일

.map()

인자값: currenValue, index, array 요소를 일괄적으로 변경

var arr = ['foo', 'hello', 'diamond', 'A']
var arr2 = arr.map((v) => v.length) // 각 요소의 글자 길이값 반환
console.log(arr2) // [3, 5, 7, 1]

.filter()

요소를 걸러내어 배열로 true/false 반환, 없으면 빈 배열

var arr = [4, 15, 377, 395, 400, 1024, 3000]
var arr2 = arr.filter((v) => (v % 5 === 0))
console.log(arr2) // [15, 395, 400, 3000]

.find()

단 하나의 요소만 반환, 여러 개 있으면 처음값만 반환

let arr = [4, 15, 377, 395, 400, 1024, 3000]
let arr2 = arr.find((n) => (n % 5 === 0))
console.log(arr2)	// 15

.reduce()

- callback

accumulator: 이전 마지막 콜백 호출에서 반환된 값 또는 공급된 경우 initialValue

currentValue: 배열 내 현재 처리되고 있는 요소(element)

currentIndex: 배열 내 현재 처리되고 있는 요소의 인덱스

array: reduce에 호출되는 배열

 

- initialValue

선택사항, callback의 첫 호출에 첫 번째 인수로 사용하는 값

let arr = [9, 2, 8, 5, 7]
let sum = arr.reduce((acc, val) => pre + val)
console.log(sum) // 31

// map
var arr = ['foo', 'hello', 'diamond', 'A']
var arr2 = arr.reduce((acc, value) => {
    acc.push(value.length)
    return pre
}, [])
console.log(arr2) // [3, 5, 7, 1]

// filter
var arr = [4, 15, 377, 395, 400, 1024, 3000]
var arr2 = arr.reduce((acc, value) => {
    if (value % 5 == 0) {
        acc.push(value);
    }
    return acc;
}, []);
console.log(arr2) // [15, 395, 400, 3000]

// find
var arr = [4, 15, 377, 395, 400, 1024, 3000]
var arr2 = arr.reduce((pre, value) => {
    if (typeof pre == 'undefined' && value % 5 == 0) {
        pre = value;
    }
    return pre;
}, undefined);
console.log(arr2)  // 15

reduce()는 위의 map, find, filter 대체 가능합니다.

 

reduce vs. map

var data = [1, 2, 3];

var initialValue = [];
var reducer = function(accumulator, value) {
  accumulator.push(value * 2);
  return accumulator;
};
var result = data.reduce(reducer, initialValue);
console.log(result); // [2, 4, 6]

// map()
var result2 = data.map(x => x * 2);
console.log(result2); // [2, 4, 6]

map 코드가 훨씬 짧고 직관적

 

reduce vs. filter

var data = [1, 2, 3, 4, 5, 6];

var initialValue = [];
var reducer = function(accumulator, value) {
  if (value % 2 != 0) {
    accumulator.push(value);
  }
  return accumulator;
};
var result1 = data.reduce(reducer, initialValue);
console.log(result1); // [1, 3, 5]

// filter
var result2 = data.filter(x => x % 2 != 0);
console.log(result2); // [1, 3, 5]

filter 역시 코드가 더 직관적

 

reduce vs. filter + map

var data = [1, 2, 3, 4, 5, 6];

var initialValue = [];
var reducer = function(accumulator, value) {
  if (value % 2 != 0) {
    accumulator.push(value * 2);
  }
  return accumulator;
}
var result1 = data.reduce(reducer, initialValue);
console.log(result1); // [2, 6, 10]

// filter + map
var result2 = data.filter(x => x % 2 != 0).map(x => x * 2);
console.log(result2); // [2, 6, 10]

하지만, map과 filter를 동시에 작업해야 한다면 reduce 는 배열을 1번만 순회하면 되지만 filter/map 조합은 두 번 순회합니다.데이터 크기, 종류에 맞는 고려 및 결정이 필요해보입니다.

 

filter/map 이 개인적으로 훨씬 더 직관적인 것으로 보이지만, reducer라는 함수로 로직이 따로 빠져있는 reduce가 더 재사용성이 좋아보입니다.

 

평균 구하기

var data = [1, 2, 3, 4, 5, 6, 1];

var reducer = (accumulator, value, index, array) => {
  var sumOfAccAndVal = accumulator + value;
  if (index === array.length - 1) {
    return (sumOfAccAndVal) / array.length;
  }
  return sumOfAccAndVal;
};

var getMean = data.reduce(reducer, 0);
console.log(getMean); // 3.142857142857143

accumulator 와 value 를 더해서 sum 을 만들고, 끝에 가서 배열의 크기로 나누는 로직입니다.

이때 초기값(initial value)을 0으로 셋팅했는데, 적지 않아도 됩니다. 그러면, 첫 번째 인자인 1 (data[0])이 accumulator로 넘어갑니다.

 

initial value 주의하기

const data = ["vote1", "vote2", "vote1", "vote2", "vote2"];

const reducer = (accumulator, value, index, array) => {
  if (accumulator[value]) {
    accumulator[value] = accumulator[value] + 1;
  } else {
    accumulator[value] = 1;
  }
  return accumulator;
};

const getVote = data.reduce(reducer, {}); // { vote1: 2, vote2: 3 } 
const getVote2 = data.reduce(reducer); // "vote1"

 

initial value 가 있고 없음에 따라 큰 차이가 나는 getVote, getVote2 를 비교한 코드입니다. getVote2 는 왜 예상과 다른 결과가 나온 이유는 reduce 메서드의 두 번째 인자로 아무 값도 전달이 되지 않았기 때문입니다.

배열의 첫 번째 값, "vote1" 이 첫 번째 순회의 accumulator 로 전달되었고, 조건문의 조건이 "vote1"["vote2"] 이 되면서 결과는 undefined(false)가 되어 "vote1"["vote"] = 1; 이라는 의미없는 로직을 한 번 타고, 결론적으로 다음 accumulator 로 또 "vote1" 이라는 string 을 넘기게 된 것입니다. 

 

flattenMap

const input = [
  {
    "title": "슈퍼맨",
    "year": "2005",
    "cast": ["장동건", "권상우", "이동욱", "차승원"]
  },
  {
    "title": "스타워즈",
    "year": "2013",
    "cast": ["차승원", "신해균", "장동건", "김수현"]
  },
  {
    "title": "고질라",
    "year": "1997",
    "cast": []
  }
];
const flatMapReducer = (accumulator, value, index, array) => {
  const key = "cast";
  if (value.hasOwnProperty(key) && Array.isArray(value[key])) {
    value[key].forEach(val => {
      if (accumulator.indexOf(val) === -1) {
        accumulator.push(val);
      }
    });
  }
  return accumulator;
};
const flattenCastArray = input.reduce(flatMapReducer, []);
// ['장동건', '권상우', '이동욱', '차승원', '신해균', '김수현']

배열을 순회하면서 배열의 값으로 들어있는 object 의 key 존재여부를 확인하고, unique 한 "cast"를 key 로 갖는 배열의 값들을 최종적으로 return 합니다.

 

reduceRight

const data = [1, 2, 3, 4, "5"];
const sumData1 = data.reduce((accumulator, value) => {
  return accumulator + value;
}, 0);
console.log(sumData1); // "105"

// reduceRight
const sumData2 = data.reduceRight((accumulator, value) => {
  return accumulator + value;
}, 0);
console.log(sumData2); // "054321"

sumData1은 0 부터 4까지 더해짐으로써 10이 되었고 마지막에 문자 "5" 와 합하게 되면서 "105" 가 return

reduceRight는 배열의 끝(오른쪽)부터 순회를 시작하는의 값, 즉 sumData2은 0 과 문자 "5" 가 첫 순회 때 + 되면서 "05" 가 되고 계속해서 string 이 더해져 "054321"을 출력합니다.

 

reduce 함수형 프로그래밍

const increment = (input) => { return input + 1; };
const decrement = (input) => { return input - 1; };
const double = (input) => { return input * 2; };
const halve = (input) => { return input / 2 };

const initial_value = 1;

// 1. 일반적일 수 있는 로직
const incremented_value = increment(initial_value);
const doubled_value = double(incremented_value);
const final_value = decrement(doubled_value);
console.log(final_value); // 3


// 2. reduce 를 활용한 함수형 프로그래밍
const pipeline = [
  increment,
  double,
  decrement,
  decrement,
  decrement,
  halve,
  double,
];
const final_value2 = pipeline.reduce((accumulator, func) => {
  return func(accumulator);
}, initial_value);
console.log(final_value2); // 1

개의 댓글