iOS/Swift

[Swift] 고차함수 (2) - flatMap, compactMap

HarryJeonn 2022. 12. 28. 20:55
 

[Swift] 고차함수 (1) - map, filter, reduce

고차함수 ? 매개변수로 함수를 갖는 함수를 말한다. Swift 표준 라이브러리에서는 다음과 같은 고차함수를 제공한다. map filter reduce 모두 컨테이너 타입(Array, Set, Dictionary 등)과 Optional 타입에서 사

harryjeon.tistory.com

저번에 map, filter, reduce에 대해서 알아봤다.

이번에는 map 중 flatMap, compactMap에 대해 알아보자.

FlatMap

선언

func flatMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

transform은 컨테이너를 매개변수로 받아 컨테이너로 반환하는 클로저이다.

flatMap의 역할은 아래와 같다.

  1. flatten하게, 단조롭게 만든다.
  2. nil을 제거한다.
  3. optional binding 해준다.

예제

let optionalNumbers = [1, nil, 3, nil, 4, 5]

let flatMapNumbers = optionalNumbers.flatMap { $0 }

// [1, 3, 4, 5]

nil을 제거한 후 배열을 반환하는 것을 알 수있다.

위와 같이 flatMap을 사용해보면

**'flatMap' is deprecated: Please use compactMap(_:) for the case where closure returns an optional value**

라는 경고문구를 확인할 수 있다. deprecated 되었으니 compactMap을 사용하라는데 compactMap에 대해 알아보자.

CompactMap

선언

func compactMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

선언부는 flatMap과 같다.

예제

let compactMapNumbers = optionalNumbers.compactMap { $0 }

// [1, 3, 4, 5]

flatMap과 compactMap의 결과값은 같다. 그럼 두 함수의 차이점은 뭘까?

FlatMap vs CompactMap

flatMap은 1차원 아래의 배열으로 반환하고 compactMap은 다차원 배열을 반환한다.

이게 무슨소리인지 직접 코드를 작성해 확인해보자.

예제1

let twoDimensionalNumbers = [[1, nil, 3], [nil, 5, 7]]

let flatMapTest = twoDimensionalNumbers.flatMap { $0 }
let compactMapTest = twoDimensionalNumbers.compactMap { $0 }

// flatMap: [Optional(1), nil, Optional(3), nil, Optional(5), Optional(7)]
// compactMap: [[Optional(1), nil, Optional(3)], [nil, Optional(5), Optional(7)]]

2차원 배열을 만들어서 각각 flatMap과 compactMap을 사용해봤다.

1차원 아래의 배열을 반환하는 flatMap은 2차원 배열인 twoDimensionalNumbers를 1차원 배열으로 변환하여 반환했다.

다차원 배열을 반환하는 compactMap은 2차원 배열 그대로 반환하는 것을 볼 수 있다.

하지만 1차원 배열에서 됐던 optional binding과 nil제거가 되지 않았다.

예제2

다차원 배열을 1차원 아래의 배열로 만들면서 optional binding 및 nil제거를 하고싶다면?

다차원 배열을 유지하면서 optional binding 및 nil제거를 하고싶다면 어떻게 해야할까?

let optionalBindingFlatMapTest = twoDimensionalNumbers.flatMap { $0.flatMap { $0 } }
let optionalBindingCompactMapTest = twoDimensionalNumbers.compactMap { $0.compactMap { $0 } }

// [1, 3, 5, 7]
// [[1, 3], [5, 7]]

Array.flatMap { $0.flatMap }

Array.compactMap { $0.compactMap }

으로 할 수 있는 것을 알게됐다.

가장 상위의 배열에서 optional binding과 nil제거를 하는 것으로 보인다.

정리

optional binding 및 nil 제거를 하고싶다면 compactMap

추가로 1차원 아래의 배열로 만들고 싶다면 flatMap

두 차이점을 알고 사용하는 것이 좋을 것 같다.