[javascript] 배열의 순환(forEach, map)

배열의 많은 메서드들은 원시타입에 적합하도록 설계 되어 있습니다.

객체가 요소인 배열에는 이런 메서드를 사용할 수 없기 때문에 배열 전체를 순환해서 원하는 값을 찾거나, 추출해야 합니다.

원시 타입에도 사용할 수 있지만, 순환 메서드인 forEach(), map() 을 활용하면 객체 배열을 원시 배열처럼 사용할 수 있습니다.

배열을 순환해 요소를 탐색하기(forEach())

배열 요소들을 탐색해 콜백 함수로 배열 요소를 처리합니다.

필요할 경우 부분 배열을 만들 수 있습니다.

객체 배열에서 age가 19 이상인 배열 요소에는 adult 값을 true로 아니면 false 로 추가합니다.

const arr1 = [{name: '라이언', age:5}, {name: '어피치', age:4}, {name:'콘', age:108}, {name: '무지', age:71}, {name:'펭수', age:2}];
arr1.forEach(function(el,idx){
  if(el.age >= 19){
    el.adult = true;
  }else{
    el.adult = false;
  }
});
/* [{adult: false, age: 5, name: "라이언"}, { adult: false, age: 4, name: "어피치"}, {adult: true, age: 108, name: "콘"}, {adult: true, age: 71, name: "무지"}, {adult: false, age: 2, name: "펭수"}] 반환*/
console.log(arr1);

앞서 adult 값이 추가된 객체 배열에서 adult가 true인 배열 요소의 인덱스 값만을 새 배열에 담아봅니다.

다만, 메서드가 배열의 순환을 통해 원 배열을 재가공하는 것에 맞춰진 만큼, 새 배열을 만드는 것은 map() 메서드를 권장합니다.

let arr2 = [];
arr1.forEach(function(el,idx){
  if(el.adult){
    arr2.push(idx); //arr2.push(el); 은 adult 값이 true인 배열 요소만을 모아서 새 객체 배열을 생성.
  }
});
console.log(arr2); // [2, 3] 반환

!주의할점

forEach() 메서드는 리턴값으로 undefined를 반환합니다. 즉, 리턴값을 사용할 수 없습니다.

같은 이유로 인해 메서드 체인을 사용할 수 없으며, 배열에 사용할 때는 단독으로만 사용해야 합니다.

forEach() 메서드는 순환을 하는 중간에 멈출 수 없습니다. 강제로 예외를 발생시켜야만 멈출 수 있습니다. 아주 큰 배열에 사용할 경우 결과를 보기 위해 불필요한 대기를 해야할 수 있습니다.


배열을 순환해 새 배열을 반환하기(map())

map() 메서드는 콜백 함수 안에서 결과 값을 반환할 수 있습니다.

반환되는 결과값 들은 하나로 모아져 새 배열로 반환되게 됩니다.

반환되는 새 배열은 변수로 받아 콜백 함수에서 처리된 결과값들를 배열 형태로 사용 수 있습니다.

사용하는 방법에 따라서는 원 배열에 영향을 주지 않고 원하는 값만 배열로 얻을 수 있기 때문에, 원본 배열을 그대로 남겨두어야 하는 경우 활용도가 높습니다.

콜백 함수 반환을 인자로 받은 요소를 그대로 반환하면 반환받은 배열은 원본 배열과 같은 배열을 참조 합니다.

map() 메서드에서 반환값을 사용할 경우 아래와 같이 콜백 함수를 만드는 것은 비효율적이며, 이런 경우에는 forEach()를 사용하는 것이 바람직합니다.

const arr2 = [{name: '라이언', age:5}, {name: '어피치', age:4}, {name:'콘', age:108}, {name: '무지', age:71}, {name:'펭수', age:2}];
const arr_mapped1 = arr2.map(function(el,idx){
  if(el.age >= 19){
    el.adult = true;
  }else{
    el.adult = false;
  }
  return el;//값이 변경된 기존 객체를 반환
});
console.log(arr_mapped1); //arr2와 같은 배열을 참조함. 배열 요소값 변경이 함께 변경됨.

위의 방법은 원 배열과 같은 배열을 참조하는 참조 변수를 만드는 것이므로 결국 아래와 같습니다.

map() 을 forEach()로 바꿀 수도 있습니다.

arr2.map(function(el,idx){
  if(el.age >= 19){
    el.adult = true;
  }else{
    el.adult = false;
  }
});
const arr_mapped2 = arr2;

map()의 장점인 반환값을 넘길 수 있는 것을 활용해, 원 배열은 그대로 두고 값이 변경된 새 객체 배열을 반환 받으려면 아래와 같이 객체 리터럴로 새 객체를 반환해야 합니다.

const arr_mapped3 = arr2.map(function(el,idx){
  let check_adult = false;
  if(el.age >= 19){
    check_adult = true;
  }
  return {name: el.name, age: el.age*2, adult: check_adult};//객체 리터럴로 새 객체를 반환
});
console.log(arr_mapped3); //arr2와 다른 배열을 참조함.

화살표 함수를 사용해 간결한 코드로 기존 배열의 이름값만 가진 새 배열을 얻고 싶은 경우 아래와 같이 만들 수 있습니다.

const arr_mapped4 = arr2.map(el=>el.name); //배열 요소 객체의 이름값만 모은 배열 반환
console.log(arr_mapped4);