본문 바로가기

Swift

flatMap() 정리하기. map(), compactMap() 과의 차이

Swift의 고차함수 중 flatMap()이라는 함수가 있다.

이 함수를 공부하면서 비슷한 이름인 map(), compactMap()과 어떤 차이가 있는지도 정리해야겠다.

flatMap() 정리하기. map(), compactMap() 과의 차이

flatMap이라는 네이밍은 flatten(평평하게 하다) + map(대응하다)가 합쳐진 의미입니다.

그래서 map의 기능을 하지만 flatten의 속성이 더해진거라고 이해할 수 있겠네요.

flatMap()은 3가지 기능이 있습니다.

 

  1. non-nil인 결과들을 가지는 배열을 리턴
  2. 주어진 Sequence내의 요소들을 하나의 배열로써 리턴
  3. 주어진 Optional이 not-nil인지 판단 후 unwrapping하여 closure 파라미터로 전달

위의 공식문서는 아래에 정리하였습니다.

 

위 각각 기능은 코드로 보면 아래와 같습니다.

let optionalArray: [Int?] = [1, 2, 3, 4, nil]
// Optinal Array :  [Optional(1), Optional(2), Optional(3), Optional(4), nil]

let flatMappedArray = optionalArray.flatMap { $0 }
// flatMapped Array :  [1, 2, 3, 4]



let nestedArray = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flatMappedNestedArray = nestedArray.flatMap { $0 }
// flatMapped Nested Array :  [1, 2, 3, 4, 5, 6, 7, 8, 9]



let optional: Int? = 8
let value = optional.flatMap { $0 > 0 ? $0 : -$0 }
// value :  Optional(8)

flatMap은 위와 같은 기능을 하고 있습니다!

그런데 하나 문제가 있습니다. 바로 첫번째, non-nil인 결과들을 가지는 배열을 리턴하는 것인데요.

코드로 작성하면 아래와 같은 경고를 발견하실 수 있습니다.

 

 

이런 과정에서는 flatMap이 deprecated되었고, compactMap을 사용하라고 나와있네요.

실제로 공식문서를 들어가보면 flatMap은 deprecated되어있고, 같은 기능을 하는 compactMap이 생겼습니다. (Swift 4.1부터 변경되었습니다.)

코드로 구현하여도 두 함수 모두 같은 결과값을 보이고 있습니다.

let optionalArray: [Int?] = [1, 2, 3, 4, nil]
// Optinal Array :  [Optional(1), Optional(2), Optional(3), Optional(4), nil]

/*
 * flatMap() vs compactMap()
 */

let flatMappedArray = optionalArray.flatMap { $0 }
// flatMapped Array :  [1, 2, 3, 4]

let compactMappedArray = optionalArray.compactMap { $0 }
// compactMapped Array :  [1, 2, 3, 4]

앞으로 flatMap을 사용할 시에 단순히 non-nil 결과값들을 얻고 싶으면 compactMap()을 사용하고

원래의 다른 목적은 그대로 flatMap을 사용하면 될 것 같습니다.

그럼 map과 flatMap은 어떤 차이가 있을까?

옵셔널로 예를 들면, 두 함수 다 옵셔널이라는 컨텍스트에서 값이라는 컨텐츠를 처리한 뒤 다시 컨텍스트에 담아 리턴하는 함수입니다.

그런데 여기서 약간의 차이가 있습니다.

먼저 함수 원형을 보겠습니다.

func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?
func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?

차이가 보이시나요? 바로 Wrapped 과정에서 flatMap은 Optional안에 값이 있다면 추출해서 진행하고 map은 그렇지 않습니다.

이 차이점 때문에 flatMap은 Optional에서 Chaining이 가능하고 map은 불가능하죠.

왜냐하면 아래 코드와 같이 map은 closure가 Optional을 반환하는 함수라면 Optional(Optional)이 되기 때문입니다.

let optionalInt: String? = "3"
let flatMappedOptionalString = optionalInt.flatMap { Int($0) }
// flatMapped Optional String :  Optional(3)

let mappedOptionalString = optionalInt.map { Int($0) }
// mapped Optional String :  Optional(Optional(3))

flatMap의 기능과 어떤 점이 compactMap으로 바뀌고 map과는 어떤 점이 차이가 있는지 공부했습니다.

솔직히 이전까지 막연하게 알고 이름으로만 차이점을 느꼈는데 이렇게 보니 내부적으로도 많은 차이가 있군요...

다음에도 이런 막연하게 알고 있던 것들을 정리해야겠습니다 :)

Reference


flatMap() - deprecated
flatMap() - Sequence
flatMap() - Optional
compactMap() - Sequence
map() - Optional