Javascript

자바스크립트 flatMap 개념 이해하기

드리프트2 2024. 8. 24. 10:56

 

JavaScript의 Array 메서드 중 하나인 flatMap을 자주 접하지만, 정확히 어떤 역할을 하는지 이해하기 어려울 때가 있습니다.

 

예제를 봐도 flatMap이 어떤 데이터 타입으로 결과를 반환하는지 명확하지 않을 때가 있었습니다.

 

그래서 이 글을 통해 flatMap의 개념을 정리하고 이해를 심화해보겠습니다.

 

공식 문서 살펴보기

flatMap

flatMap()Array 인스턴스의 메서드로, 각 요소에 맵핑 함수를 적용한 후 결과를 새로운 배열로 평탄화하는 기능을 제공합니다.

 

이는 map()을 사용한 후 깊이 1의 flat()을 호출하는 것과 동일하지만 (arr.map(...args).flat()), 두 메서드를 각각 호출하는 것보다 약간 더 효율적입니다.

 

공식 문서는 위와 같이 설명하고 있지만, "평탄화"라는 개념이 낯설고, "약간 효율적"이라는 부분이 어떤 의미인지 의문이 들었습니다.

 

flat의 문서도 살펴보면 다음과 같습니다.

 

flat

flat()Array 인스턴스의 메서드로, 모든 하위 배열 요소를 지정된 깊이까지 재귀적으로 결합한 새로운 배열을 생성합니다.

 

이 설명 역시도 낯설고 이해하기 어려웠습니다. 이를 좀 더 명확히 이해해 보겠습니다.

 

flat 이해하기

flat의 예시

flat의 개념을 이해하기 위해 문서에 나와 있는 예시를 지역, 도시, 구역, 거리라는 계층으로 바꾸어 생각해 보겠습니다.

// 문서의 예시
const arr2 = [1, 2, [3, 4, [5, 6]]];
console.log(arr2.flat());
// [1, 2, 3, 4, [5, 6]]

// 계층 구조로 변환
const arr2 = ['대한민국', '일본', ['서울', '부산', ['인천', '대구']]];
console.log(arr2.flat());
// ['대한민국', '일본', '서울', '부산', ['인천', '대구']]

 

최상위 계층이 "국가"인 배열에 flat()을 사용하면, 2계층에 있었던 "도시" 요소가 1계층의 "국가"와 같은 수준으로 올라옵니다.

 

단순히 한 계층을 분해하는 이미지입니다. 이렇게 계층을 단순화하는 것이 flat()의 역할입니다.

 

flatMap 이해하기

flatMap의 필요성

flatMap을 사용하고자 할 때는 중첩된 계층의 요소에 대해 특정 처리를 수행하고 싶을 때입니다.

 

예를 들어, 다음과 같은 복잡한 처리가 필요하다고 가정해 봅시다.

 

문제: 인구 50만 명 이상인 도시를 출력하라.

조건: 아래 regions라는 객체가 주어짐.

const regions = [
  {
    prefecture: '서울',
    cities: [
      { city: '강남구', population: 339000 },
      { city: '서초구', population: 227000 },
      { city: '송파구', population: 939000 }
    ]
  },
  {
    prefecture: '부산',
    cities: [
      { city: '해운대구', population: 2750000 },
      { city: '남구', population: 830000 },
      { city: '동구', population: 500000 }
    ]
  },
  {
    prefecture: '대구',
    cities: [
      { city: '달서구', population: 400000 },
      { city: '북구', population: 160000 },
      { city: '남구', population: 90000 }
    ]
  }
];

map()만 사용한 경우

const largeCities = regions.map(region => 
  region.cities.filter(city => city.population >= 500000)
);

console.log(largeCities);

 

위 코드에서 map()만 사용했을 때는 다음과 같이 중첩된 배열이 반환됩니다.

[
  [
    { city: '송파구', population: 939000 }
  ],
  [
    { city: '해운대구', population: 2750000 },
    { city: '남구', population: 830000 },
    { city: '동구', population: 500000 }
  ],
  [] // 대구의 처리 결과
]

 

이렇게 중첩된 배열은 타입스크립트 같은 정적 타입 언어에서는 타입 오류를 발생시킬 수 있습니다.

 

이를 해결하기 위해 flatMap의 사용이 필요합니다.

map() 후 flat() 실행

const largeCities = regions.map(region => 
  region.cities.filter(city => city.population >= 500000)
).flat();

console.log(largeCities);

 

map()flat()을 실행하면 중첩된 배열이 평탄화되어 다음과 같은 결과를 얻을 수 있습니다.

 

[
  { city: '송파구', population: 939000 },
  { city: '해운대구', population: 2750000 },
  { city: '남구', population: 830000 },
  { city: '동구', population: 500000 }
]

 

이처럼 원하는 결과를 얻을 수 있지만, 매번 flat()을 호출하는 것은 번거로울 수 있습니다. 이때 flatMap()이 유용합니다.

 

flatMap() 사용

const largeCities = regions.flatMap(region => 
  region.cities.filter(city => city.population >= 500000)
);

console.log(largeCities);

 

flatMap()을 사용하면 map()flat()을 호출한 것과 동일한 결과를 얻을 수 있습니다.

 

[
  { city: '송파구', population: 939000 },
  { city: '해운대구', population: 2750000 },
  { city: '남구', population: 830000 },
  { city: '동구', population: 500000 }
]

 

이제 flatMap()의 기능을 이해하고 나니 공식 문서를 다시 보면 그 의미가 명확해집니다.

 

flatMap()은 배열의 각 요소를 매핑한 후 결과를 새로운 배열에 평탄화합니다.

 

이는 map() 후 깊이 1의 flat()을 수행하는 것과 동일하지만, 두 메서드를 별도로 호출하는 것보다 약간 더 효율적입니다.

 

추가 정보

flatMap()은 평탄화 깊이를 지정할 수 없지만, 더 깊은 계층까지 평탄화하고 싶다면 flat()을 사용하여 다음과 같이 지정할 수 있습니다.

const arr3 = [1, 2, [3, 4, [5, 6]]];
console.log(arr3.flat(2));
// [1, 2, 3, 4, 5, 6]

const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
console.log(arr4.flat(Infinity));
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

 

이러한 기능은 언젠가 유용하게 사용할 수 있을 것입니다. 기억해 두면 좋습니다.


이렇게 새롭게 작성된 글을 통해 flatMap의 개념과 사용법을 이해할 수 있기를 바랍니다.

 

그럼.