[번역] bts 봉준호 손흥민 Array.reduce()

Moon Ki, Kim·2024년 7월 7일
0

원문 링크

Array.reduce() is Goated 🐐

제목이 모든 것을 말해줍니다 🐐. 제가 가장 좋아하는 자바스크립트 배열 메서드: Array.reduce()에 대해 이야기해보려고 합니다. 많은 경쟁자들이 있지만, 제 말을 들어보세요. reduce()는 단순한 메서드가 아닙니다. 그것은 일종의 생활 방식이에요✨.

처음에 reduce를 발견했을 때는 좀 겁먹었지만, 사용법을 확실히 익히고 나니 완전히 게임 체인저였습니다. 갑자기 배열에서 복잡한 연산을 쉽게 수행할 수 있게 되었고, 제 코드가 더 빠르고 깔끔해졌습니다.

하지만 제 말만 믿지 마세요. reduce()로 무엇을 할 수 있는지 몇 가지 예를 보여드릴게요. 이제 Array.reduce()의 매력에 빠져봅시다! 🐐

Array.reduce()의 9가지 사용 사례 🐐

사용 사례 1: 숫자 합산

reduce()의 가장 간단한 사용 사례 중 하나는 여러 숫자를 합산하는 것입니다. 예를 들어, 정수 배열이 있고 총합을 구하고 싶다고 가정해봅시다.

const numbers: number[] = [1, 2, 3, 4, 5];
const sum: number = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // Output: 15

짜잔! 한 줄의 코드로 배열의 모든 요소의 합을 계산했습니다. 누산기(Accumulator)의 초기 값은 0으로 설정하고, 각 반복에서 현재 요소를 누산기에 더합니다.

  • 보너스: 초기 값을 생략하면 reduce는 배열의 첫 번째 항목을 사용합니다. 그러나 가독성을 위해 항상 초기 값을 포함하는 편입니다.

사용 사례 2: 배열 평탄화

배열 안에 배열이 있어서 이를 하나의 배열로 평탄화하고 싶었던 적이 있나요?

const nestedArray: number[][] = [[1, 2], [3, 4], [5, 6]];
const flattenedArray: number[] = nestedArray.reduce((acc, curr) => acc.concat(curr), []);
console.log(flattenedArray); // Output: [1, 2, 3, 4, 5, 6]

이 예에서 우리는 빈 배열을 초기 누산기 값으로 시작합니다. 그런 다음 각 반복 concat() 메서드를 사용하여 현재 하위 배열을 누산기에 연결합니다. 결과는 완벽하게 평탄화된 배열입니다.

Array.flat()을 사용할 수도 있지만, 각 항목에 대해 추가 작업을 수행해야 할 경우를 대비해 reduce를 사용하는 방법을 아는 것이 중요합니다.

사용 사례 3: 객체 그룹화

특정 속성을 기준으로 객체 배열을 그룹화해야 한다고 가정해봅시다. reduce()가 이 작업에 완벽한 도구입니다.

tsx코드 복사
interface Person {
  name: string;
  age: number;
}

const people: Person[] = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 25 },
  { name: 'Dave', age: 30 }
];

const groupedByAge: { [key: number]: Person[] } = people.reduce((acc, curr) => {
  if (!acc[curr.age]) {
    acc[curr.age] = [];
  }
  acc[curr.age].push(curr);
  return acc;
}, {});

console.log(groupedByAge);
/*
Output:
{
  '25': [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }],
  '30': [{ name: 'Bob', age: 30 }, { name: 'Dave', age: 30 }]
}
*/

이 경우, 객체를 초기 누산기 값으로 사용합니다. 누산기에 현재 나이에 대한 속성이 이미 있는지 확인합니다. 없다면 해당 나이에 대한 빈 배열을 만듭니다. 그런 다음 현재 객체를 해당 나이 배열에 추가합니다. 결과적으로 나이를 키로 하고, 값을 그 나이의 사람들로 이루어진 배열로 가지는 객체를 얻습니다.

이제 새로운 groupBy 메서드도 사용할 수 있지만, 이 고전적인 방법을 이해하는 것이 중요합니다.

사용 사례 4: 조회 맵 생성

개인적으로 가장 좋아하는 것은 reduce()를 사용하여 배열에서 조회 맵을 생성하는 것입니다. 성능과 코드 가독성 면에서 게임 체인저입니다. 느린 find() 또는 filter() 호출을 그만 사용하세요.

interface Product {
  id: number;
  name: string;
  price: number;
}

const products: Product[] = [
  { id: 1, name: 'Laptop', price: 999 },
  { id: 2, name: 'Phone', price: 699 },
  { id: 3, name: 'Tablet', price: 499 },
];

const productMap: { [key: number]: Product } = products.reduce((acc, curr) => {
  acc[curr.id] = curr;
  return acc;
}, {});

console.log(productMap);
/*
Output:
{
  '1': { id: 1, name: 'Laptop', price: 999 },
  '2': { id: 2, name: 'Phone', price: 699 },
  '3': { id: 3, name: 'Tablet', price: 499 }
}
*/

// ID로 제품 접근하기
const laptop: Product = productMap[1];
console.log(laptop); // Output: { id: 1, name: 'Laptop', price: 999 }

reduce()를 사용하여 조회 맵을 생성하면 고유 식별자로 요소를 상수 시간 복잡도로 접근할 수 있습니다. 특정 항목을 찾기 위해 배열을 반복할 필요가 없습니다.

사용 사례 5: 발생 횟수 세기

배열에서 요소의 발생 횟수를 세야 했던 적이 있나요? reduce()가 도와줄 수 있습니다.

const fruits: string[] = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];

const fruitCounts: { [key: string]: number } = fruits.reduce((acc, curr) => {
  acc[curr] = (acc[curr] || 0) + 1;
  return acc;
}, {});

console.log(fruitCounts);
/*
Output:
{
  'apple': 3,
  'banana': 2,
  'orange': 1
}
*/

이 예에서 우리는 빈 객체를 초기 누산기 값으로 설정합니다. 각 과일에 대해 누산기 객체에 해당 과일이 속성으로 존재하는지 확인합니다. 존재하면 그 값을 1 증가시키고, 그렇지 않으면 1로 초기화합니다. 결과는 배열에서 각 과일이 몇 번 나타나는지 알려주는 객체입니다.

사용 사례 6: 함수 조합

함수형 프로그래밍 애호가들은 이 예를 좋아할 것입니다. reduce()는 함수를 조합하는 데 강력한 도구입니다. 데이터를 단계별로 변환하는 함수 파이프라인을 만들 수 있습니다.

const add5 = (x: number): number => x + 5;
const multiply3 = (x: number): number => x * 3;
const subtract2 = (x: number): number => x - 2;

const composedFunctions: ((x: number) => number)[] = [add5, multiply3, subtract2];

const result: number = composedFunctions.reduce((acc, curr) => curr(acc), 10);
console.log(result); // Output: 43

이 예에서 우리는 순서대로 적용하고 싶은 함수 배열이 있고, 초기 값 10을 설정합니다. reduce()를 사용하여 각 함수를 반복하고, 각 함수의 결과를 다음 함수에 입력으로 전달합니다. 최종 결과는 모든 함수를 조합한 결과입니다.

사용 사례 7: 간단한 Redux 스타일 상태 관리 구현

Redux를 사용해본 적이 있다면, 애플리케이션 상태 관리를 얼마나 강력하게 할 수 있는지 알 것입니다. 놀랍게도 reduce()를 사용하여 간단한 Redux 스타일 상태 관리 시스템을 구현할 수 있습니다.

interface State {
  count: number;
  todos: string[];
}

interface Action {
  type: string;
  payload?: any;
}

const initialState: State = {
  count: 0,
  todos: [],
};

const actions: Action[] = [
  { type: 'INCREMENT_COUNT' },
  { type: 'ADD_TODO', payload: 'Learn Array.reduce()' },
  { type: 'INCREMENT_COUNT' },
  { type: 'ADD_TODO', payload: 'Master TypeScript' },
];

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'INCREMENT_COUNT':
      return { ...state, count: state.count + 1 };
    case 'ADD_TODO':
      return { ...state, todos: [...state.todos, action.payload] };
    default:
      return state;
  }
};

const finalState: State = actions.reduce(reducer, initialState);
console.log(finalState);
/*
Output:
{
  count: 2,
  todos: ['Learn Array.reduce()', 'Master TypeScript']
}

이 예에서 우리는 초기 상태 객체와 액션 배열이 있습니다. 현재 상태와 액션을 받아들이고, 액션 유형에 따라 새로운 상태를 반환하는 리듀서 함수를 정의합니다. reduce()를 사용하여 각 액션을 순차적으로 상태에 적용하여 최종 상태를 얻습니다. 작은 Redux와도 같습니다.

사용 사례 8: 고유 값 생성

때때로 중복 값이 있는 배열에서 고유한 값만 추출해야 할 때가 있습니다. reduce()가 쉽게 도와줍니다.

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

const uniqueNumbers: number[] = numbers.reduce((acc, curr) => {
  if (!acc.includes(curr)) {
    acc.push(curr);
  }
  return acc;
}, []);

console.log(uniqueNumbers); // Output: [1, 2, 3, 4, 5, 6]

여기서 우리는 빈 배열을 초기 누산기 값으로 설정합니다. 원래 배열의 각 숫자에 대해, includes() 메서드를 사용하여 누산기에 이미 존재하는지 확인합니다. 존재하지 않으면 누산기 배열에 추가합니다. 최종 결과는 원래 배열에서 고유 값만 포함된 배열입니다.

사용 사례 9: 평균 계산

숫자 집합의 평균을 계산하고 싶으신가요? reduce()가 도와줄 수 있습니다!

const grades: number[] = [85, 90, 92, 88, 95];

const average: number = grades.reduce((acc, curr, index, array) => {
  acc += curr;
  if (index === array.length - 1) {
    return acc / array.length;
  }
  return acc;
}, 0);

console.log(average); // Output: 90

이 예에서 우리는 초기 누산기를 0으로 설정합니다. 각 성적을 반복하여 누산기에 더합니다. 마지막 요소에 도달하면, 누산기를 총 성적 수로 나누어 평균을 계산합니다.

성능 고려 사항 🏎️

Array.reduce()는 매우 강력하고 다재다능하지만, 특히 대규모 배열이나 복잡한 연산을 다룰 때 성능 문제를 염두에 두어야 합니다. 하나의 일반적인 함정은 reduce()의 각 반복에서 새로운 객체나 배열을 생성하는 것입니다. 이는 과도한 메모리 할당을 유발하고 성능에 영향을 미칠 수 있습니다.

예를 들어, 다음 코드를 고려해보세요:

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

const doubledNumbers: number[] = numbers.reduce((acc, curr) => {
  return [...acc, curr * 2];
}, []);

console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]

이 경우, 우리는 스프레드 연산자(...)를 사용하여 각 반복에서 새로운 배열을 생성합니다. 이는 비효율적일 수 있습니다. 대신, 누산기 배열을 직접 변경하여 코드를 최적화할 수 있습니다:

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

const doubledNumbers: number[] = numbers.reduce((acc, curr) => {
  acc.push(curr * 2);
  return acc;
}, []);

console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]

push()를 사용하여 누산기 배열을 변경함으로써, 각 반복에서 새로운 배열을 생성하는 것을 피하고 성능을 향상시킵니다.

유사하게, 객체를 다룰 때도 스프레드 연산자 대신 누산기 객체를 직접 변경하는 것이 더 효율적입니다:

const people: Person[] = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 25 },
  { name: 'Dave', age: 30 }
];

const groupedByAge: { [key: number]: Person[] } = people.reduce((acc, curr) => {
  if (!acc[curr.age]) {
    acc[curr.age] = [];
  }
  acc[curr.age].push(curr);
  return acc;
}, {});

누산기 객체를 직접 변경함으로써 reduce() 연산의 성능을 최적화할 수 있습니다.

그러나, 어떤 경우에는 각 반복에서 새로운 객체나 배열을 생성하는 것이 필요하거나 더 읽기 쉬울 수 있습니다. 특정 사용 사례와 데이터 크기에 따라 성능과 코드 명확성 사이의 균형을 맞추는 것이 중요합니다.

결론

이상입니다. Array.reduce()의 힘과 다재다능함을 보여주는 9가지 놀라운 사용 사례를 소개했습니다. 숫자 합산부터 배열 평탄화, 객체 그룹화, 조회 맵 생성, 발생 횟수 세기, 함수 조합, 상태 관리 구현, 고유 값 생성, 평균 계산까지, Array.reduce()는 여러분의 자바스크립트 도구 상자에서 강력한 도구임을 입증합니다.

어떻게 생각하시나요? 여러분이 가장 좋아하는 배열 메서드는 무엇이고, 왜 그렇게 생각하시나요?

읽어주셔서 감사합니다. reduce()의 힘이 여러분과 함께하기를 바랍니다. ✨🐐✨

마지막으로 하나만 더 말하자면 😁

애자일 개발 팀에서 일하신다면, 제 무료 플래닝 포커 및 회고 앱 Kollabe를 확인해보세요.

0개의 댓글