개발일지/Front-end

Javascript 배열 용도에 맞게 순회하기

cotnmin 2023. 1. 29. 21:30

Javascript에서 순회를 사용할 때 주로 forEach를 사용합니다.
하지만 forEach는 반복도중 중단을 할 수 없다는 단점이 있습니다.
이를 for 문을 이용해 해결할 수 있지만 좀 더 깔끔하고 명확하게 쓸 수 있는 몇 가지 메서드를 알아보겠습니다

1. Array.prototype.some()과 Array.prototype.every()

some과 every는 각 배열을 순회하며 주어진 조건의 반환값에 참이 존재하는지, 모든 값이 참인지 알려주는 메서드입니다.
some은 참을 만나면 순회를 중단하고 참을 반환하며 순회를 마칠 때까지 참이 나오지 않으면 거짓을 반환합니다.
every는 거짓을 만나면 순회를 중단하고 거짓을 반환하며 순회를 마칠 때까지 거짓이 나오지 않으면 참을 반환합니다.

const arr = ["apple", "banana", 3, "orange"];

const hasApple = arr.some(item => item == "apple");
const allString = arr.every(item => typeof item == "string");

console.log(`hasApple? ${hasApple}`);
// hasApple? true
console.log(`allString? ${allString}`);
// allString? false

위와 같이 사용할 수 있습니다. 같은 방식으로 for문이나 forEach를 사용하면

const arr = ["apple", "banana", 3, "orange"];

let hasApple = false;
for (let i = 0; i < arr.length; i++) {
   if ( arr[i] == "apple") {
       hasApple = true;
       break;
   }
}

let allString = true;
arr.forEach(item => {
    if (typeof item == "string") allString = false;
});

console.log(`hasApple? ${hasApple}`);
// hasApple? true
console.log(`allString? ${allString}`);
// allString? false

위와 같이 쓸 수 있는데 forEach를 쓴 경우 타입이 문자열이 아닌 아이템을 찾아도 순회를 중단할 수 없는 단점이 있습니다. for문을 쓰는 것도 좋은 방식이지만 some이나 every를 사용하는 게 좀 더 명확하게 사용하는 방법이라고 생각합니다.
사실 값의 유무를 찾는 것뿐이라면 indexOf 나 includes를 사용하는 게 깔끔하지만 예시일 뿐입니다.

2. Array.prototype.map()

React를 많이 써보신 분들은 많이 써보셨을 map입니다.
이 메서드는 주어신 함수를 실행해 반환된 값을 배열에 담아 반환해 주는 함수입니다.

const arr = [1, 2, 3, 4];
console.log(arr.map(item => item * 2));
//  [2, 4, 6, 8]

만약 해당 함수를 for문이나 forEach를 이용하게 되면 새 배열을 담을 변수를 할당해야 합니다. react에서 jsx를 사용할 때처럼 굳이 새 변수에 할당할 필요가 없을 때 유용합니다.

3. Array.prototype.find()와 Array.prototype.findIndex()

단순히 인덱스를 찾은거라면 indexOf를 사용하는 것이 좋지만 해당 값을 사용하거나 객체가 담긴 배열을 순회하며 해당 객체에 값을 찾는 경우 유용하게 사용할 수 있습니다.

const arr = [
    {
        id: 1,
        en: 'apple',
        ko: '사과'
    },
    {
        id: 2,
        en: 'banana',
        ko: '바나나'
    },
    {
        id: 3,
        en: 'orange',
        ko: '오렌지'   
    }
];

const findApple = arr.find(item => item.en == 'apple');
const findOrangeIndex = arr.findIndex(item => item.ko == '오렌지');

console.log(findApple);
/*
{
    id: 1,
    en: 'apple',
    ko: '사과'
}
*/
console.log(findOrangeIndex);
// 2

4. Array.prototype.reduce()

배열 메서드의 끝판왕 reduce입니다.
array.reduce(callback, init);
callback함수는 (prev, curr, index, arr) => res 같은 형태로

  • prev: 이전 callback의 결과입니다.
  • curr: 현재 요소입니다.
  • index: 현재 요소의 인덱스입니다.
  • arr: 현재 탐색 중인 배열입니다.
  • res: 계산결과로 다음 callback의 prev에 들어갑니다.

init은 초기값으로 첫 callback의 prev값이 됩니다.
만약 init값을 설정하지 않으면 첫 번째 배열 요소를 prev로 사용하고 두 번째 배열부터 탐색합니다.

const arr = [1, 2, 3, 4, 5];
const sum = arr.reduce((prev, curr) => prev + curr);
// 1회: (1, 2) => 3
// 2회: (3, 3) => 6
// 3회: (6, 4) => 10
// 4회: (10, 5) => 15

console.log(sum);
// 15

위와 같이 누산이 필요한 경우 활용할 수 있습니다.
다만, 빈 배열에서 init이 없이 호출하는 경우 error를 일으키므로 init을 전달해 주는 게 안전합니다.


const  arr = [3, 2, -5,  5, 10, 7, -2];
const [sum, min, max] = arr.reduce(([sum, min, max], curr) => [sum + curr, Math.min(min, curr), Math.max(max, curr)], [0, Infinity, -Infinity]);

console.log(sum, min, max);
// 20, -5, 10

이렇게 사용하는 것도 가능합니다.
사실 이쯤되면 그냥 for문이나 forEach를 쓰는 게 가독성이 좋긴 하지만 웬만한 반복문을 3항 연산자와 reduce만 이용한다면 중괄호 없이 한 줄로 끝내버릴 수 있습니다.

가끔 코테를 풀 때
if문 -> 3항 연산자
반복문 -> 배열 메서드
+ spread 연산자, 구조분해할당 등등
을 이용해 극한의 한줄만들기를 하면 나름 재미있습니다.

오늘도 읽어주셔서 감사하고 문제있는 내용은 댓글로 알려주세요!!

참고

  • https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array
  • https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce