220914 TIL forEach(), map() 그리고 filter()

어젯밤에 자바스크립트를 자바처럼 사용하고 있다는 이야기를 해주셨다.

아직까지도 자바스크립트가 익숙하지 않아서 그런 것도 있지만 4개월 정도 주구장창 자바만 작성하던 몸이라 그런지 자바스크립트로 어떻게 코드를 작성해야 할지 모르겠을 때 본능적으로 자바에서 썼던 방법을 쓰려했던 것 같다.

 

그리고 자바스크립트에서는 for를 쓸 일이 없다는 이야기를 듣고 놀랐다.

사용하기 단순해서 제일 많이 사용하는 for문을 쓸 일이 없다니..

그럼 뭐를 사용해야 하는 의문과 동시에 바로 해결방안을 말씀해주셨다.

자바스크립트는 forEach, map, reduce 이 3개만 제대로 사용하면 대부분 해결이 가능하다고 하셨다.

 

하지만 저 3개 중 제대로 사용할 수 있는 건 단 하나도 없었다. 

저 3개 중에 그나마 map을 todolist 만들면서 제일 많이 사용해서 어느 정도는 알고 있다고 생각을 했는데, 오늘 map을 이용하는데 내가 알던 map이 map이 아니라는 걸 깨닫고 map마저 제대로 알지 못하고 있다는 것을 인지했다.

 

나의 상태가 심각하다는 것을 깨닫고 빨리 하나씩 공부를 하기로 했다.

 

forEach()

forEach 메서드는 주어진 함수를 배열 요소 각각에 대해 실행할 수 있게 해주는 메서드이다. (mdn)

즉, for문 대신 forEach로도 배열 안의 값을 하나씩 반복해서 출력이 가능하다.

 

기존에 내가 for문을 이용해서 배열의 값을 하나씩 출력했던 방법

const numbers = [1, 2, 3, 4, 5];

for (let i = 0; i < numbers.length; i += 1) {
  console.log(numbers[i]);
}

// 1, 2, 3, 4, 5

 

forEach를 이용해서 배열의 값을 하나씩 출력하는 방법

const numbers = [1, 2, 3, 4, 5];

numbers.forEach((element) => {
  console.log(element);
});

// 1, 2, 3, 4, 5

 

여기서 element에 numbers 배열안의 값이 하나씩 들어가서 함수(여기서는 console.log())를 실행한다.

결국 반복문처럼 배열안의 모든 요소를 한 번씩 돌면서 콜백 함수 또는 익명 함수를 실행한다.

 

선택적 매개변수(Index)

선택적 매개변수인 index는 forEach에 매개변수로 들어온 요소(element)의 index(순서) 값도 알 수 있다.

const numbers = [1, 2, 3, 4, 5];

numbers.forEach((element, index) => {
  console.log(element, index);
});

// 1 0
// 2 1
// 3 2
// 4 3
// 5 4

index의 매개변수로 element의 요소가 numbers배열에서 몇 번째에 위치하고 있는지 index매개변수로 확인이 가능하다.

결과를 보면 첫 번째 요소인 "1"이 number의 배열에서 0번째에 위치하고 있다는 것을 알려주는 걸 확인할 수 있다.

 

선택적 매개변수(Array)

심지어 매개변수로 배열을 줘서 원본 배열을 확인할 수 있다.

const numbers = [1, 2, 3, 4, 5];

numbers.forEach((element, index, array) => {
  console.log(element, index, array);
});

// 1 0 Array [1, 2, 3, 4, 5]
// 2 1 Array [1, 2, 3, 4, 5]
// 3 2 Array [1, 2, 3, 4, 5]
// 4 3 Array [1, 2, 3, 4, 5]
// 5 4 Array [1, 2, 3, 4, 5]

배열을 매개변수로 주면 원본 배열인 numbers를 그대로 반환하는 것을 알 수 있다.

 

 

map()

그러면 map은 뭘까?

map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환한다. (mdn)

 

map 메서드도 forEach와 비슷하게 배열을 순회하면서 주어진 함수를 실행하는 것 같다.

여기서 차이점은 map()은 새로운 배열을 반환한다는 것이다.

설명보다는 역시 코드를 한번 쳐보는 게 이해가 훨씬 빠르니 한번 쳐보자! 백문이불여일타!!

const numbers = [1, 2, 3, 4, 5];

const newNumbers = numbers.map((element) => element + 10);

console.log(numbers);
console.log(newNumbers);

// numbers = Array [1, 2, 3, 4, 5]
// newNumbers = Array [11, 12, 13, 14, 15]

forEach메서드와 비슷하게 numbers의 요소를 하나씩 꺼내와서 element에 담아 함수(여기서는 element + 10)를 실행한다.

각각의 요소 하나에 대하여 함수를 하나씩 실행한 결과를 새로운 배열(newNumbers)에 담는다.

 

결국 출력된 값을 보면 기존 배열인 numbers는 원본 데이터를 유지하고, map() 메서드를 거친 newNumbers배열은 모두 10이 증가된 값이 들어간 것을 확인할 수 있다.

 

map 또한 forEach처럼 두 번째, 세 번째 매개변수를 줘서 요소의 index와 원본 array를 얻을 수 있다.

const numbers = [1, 2, 3, 4, 5];

const newNumbers = numbers.map((element, index, array) => {
  console.log(element, index, array);
});

// 1 0 Array [1, 2, 3, 4, 5]
// 2 1 Array [1, 2, 3, 4, 5]
// 3 2 Array [1, 2, 3, 4, 5]
// 4 3 Array [1, 2, 3, 4, 5]
// 5 4 Array [1, 2, 3, 4, 5]

 

map에서 가장 중요한 점은 새로운 배열을 반환한다는 것이다.

즉, map을 이용해서 어떤 배열의 값을 변경할 때 배열의 원본을 바꾸는 것이 아닌(재할당) 새로운 배열을 반환하고 원본 배열은 유지한다는 것이다.

 

오늘 이 재할당 관련해서 코드 리뷰를 받은 게 있어 map을 사용해서 새로운 배열을 리턴하려 했지만 문제가 있었다.

기존에 내가 배열을 바꾸려 했던 상황은 forEach를 이용해서 특정 아이디 값이 배열 안의 요소의 아이디 값과 같으면 배열 안의 그 값을 삭제하는 방식이었다. 즉 원본 배열의 값을 삭제하기 때문에 재할당하는 형태였다.

 

 

그래서 map을 이용해서 원본 배열의 값은 유지한 채 특정 조건(배열 안의 아이디 값과 특정 아이디 값이 같을때)이 만족하면 그 요소는 제외하고 새로운 배열을 만들고 싶었는데 아무리 생각해도 map은 배열안의 모든 요소를 순회한 뒤 새로운 배열을 리턴하기 때문에 어떤 특정한 요소만 제외할 수는 없다고 생각했다. (틀렸다면 피드백 부탁드립니다..)

 

그래서 map과 같이 배열을 돌면서 특정 조건이 만족하면 그 요소는 제외하고 새로운 배열을 만드는 방법이 없는지 찾아보다 filter()라는 메서드를 찾게 되었다.

filter()

mdn의 설명을 보면 filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환한다고 한다. (mdn)

여기서 key point는 주어진 함수의 테스트 즉, 어떤 조건을 통과하는 요소들만 모아서 배열을 만들어 준다고 한다.

완전 내가 찾고 있던 메서드였다.

 

 

 

배열을 돌면서 요소의 아이디값과 특정 아이디 값이 다른 요소들만 모아진 새로운 배열(filteredList)을 만들었다.

기존에 forEach를 사용하던 게 filter를 이용해서 원본 배열은 건드리지 않고 새로운 배열(filteredList)을 만들 수 있었다.

 

 

자바보다 자바스크립트가 더 코딩테스트 문제 풀 때 간단하다고 하시는데 아직은 와닿지 않지만..

코딩도장 문제 풀때 자바보다 자바스크립트에 먼저 손이 가는 그날까지..