이 포스팅은 Swift 시리즈 20 편 중 12 번째 글 입니다.

  • Part 1 - 01: Optional, Any, AnyObject, nil
  • Part 2 - 02: struct, class, enum
  • Part 3 - 03: Closure
  • Part 4 - 04: Property
  • Part 5 - 05: 상속, 생성자, 소멸자
  • Part 6 - 06: 옵셔널 체이닝과 nil 병합
  • Part 7 - 07: 타입 캐스팅
  • Part 8 - 08: assert, guard
  • Part 9 - 09: Protocol
  • Part 10 - 10: Extention
  • Part 11 - 11: 오류처리
  • Part 12 - This Post
  • Part 13 - 13: ARC(Automatic Reference Counting)
  • Part 14 - 14: Access control, Access modifier
  • Part 15 - 15: Generics
  • Part 16 - 16: Optional에 대한 깊은 이해
  • Part 17 - 17: Lazy Variables
  • Part 18 - 18: Enumeration
  • Part 19 - 19: Initialization
  • Part 20 - 20: Concurrency
▼ 목록 보기

고차 함수

  • 다른 함수를 전달인자로 받거나, 함수 실행의 결과를 함수로 반환하는 함수
  • 여기서 중요한 것은, 클로저는 일급 객체(시민)이기 때문에 전달인자, 결과값으로 반환이 가능하다.

map

  • 컨테이너(array, dictionary, set) 내부의 기존데이터를 변형하여 새로운 컨테이너를 생성
let numbers: [Int] = [0, 1, 2, 3, 4]
var doubledNumbers: [Int]
var strings: [String]

// 기존의 방식
doubledNumbers = [Int]()
strings = [String]()
for number in numbers{
    doubledNumbers.append(number * 2)
    strings.append("\(number)")
}

// map 사용
doubledNumbers = [Int]()
strings = [String]()
doubledNumbers = numbers.map({ (number: Int) -> Int in
    return number * 2
})
strings = numbers.map({(number: Int) -> String in
    return "\(number)"
})

// 매개 변수 생략, 반환 키워드 생략, 후행 클로저 사용
doubledNumbers = numbers.map { $0 * 2 }
strings = numbers.map { "\($0)" }

CompactMap

  • Nil을 지우면서 map 작업 가능
let numbersWithNil = [5, 15, nil, 3, 9, 12, nil, nil, 17, nil]
// 이 상황에서 그냥 각각에 대해 연산을 수행하면 에러가 난다.
// 아래와 같이 후처리를 해주어야 한다.
let doubledNums = numbersWithNil.map { $0 != nil ? $0! * 2 : nil }
    
print(doubledNums)
// 출력 결과를 보면 nil이 섞여 있다. 다시 filter를 통해 걸러주어야 한다.
// [Optional(10), Optional(30), nil, Optional(6), Optional(18), Optional(24), nil, nil, Optional(34), nil]

// 이 두작업을 한번에 할 수 있다.
let notNilDoubled = numbersWithNil.compactMap { $0 != nil ? $0! * 2 : nil }
print(notNilDoubled) // [10, 30, 6, 18, 24, 34]

flatMap

  • Flat하게 만들어주는 녀석
// 2차원인 경우에 유용하게 사용이 가능하다!
let marks = [[3, 4, 5], [2, 5, 3], [1, 2, 2], [5, 5, 4], [3, 5, 3]]

// for 문을 사용해서 flat하게 만드는 방법이다.
var allMarks = [Int]()
for marksArray in marks {
    allMarks += marksArray
}

// 아래와 같은 두 방법으로 사용이 가능하다.
let allMarks2 = marks.flatMap { (array) -> [Int] in
    return array
}

let allMarks3 = marks.flatMap { $0 }

print(allMarks3)
// Prints [3, 4, 5, 2, 5, 3, 1, 2, 2, 5, 5, 4, 3, 5, 3]

Nil 지우기

  • compactMap flatMap 등이 증정품으로 있다.
    • 둘다 nil이 아닌 녀석을 뽑아서 컨테이너를 생성해준다.
    • 1차원 배열에서는 두 녀석 동작이 모두 같다.
    • 하지만 2차원 배열의 경우 flatmap만 동작하여 결과값을 1차원으로 준다.
    • compactmap의 경우 동작하지 않는다.
let marksArray = [[3, nil, 5], [2, 5, 3], [1, nil, nil], [5, 5, nil], [3, 5, 3]]

let notNilMarksFlated = marksArray.flatMap { $0 }
print(notNilMarksFlated)
// [Optional(3), nil, Optional(5), Optional(2), Optional(5), Optional(3), Optional(1), nil, nil, Optional(5), Optional(5), nil, Optional(3), Optional(5), Optional(3)]

let notNilMarksCompact = marksArray.compactMap { $0 }
print(notNilMarksCompact)
// [[Optional(3), nil, Optional(5)], [Optional(2), Optional(5), Optional(3)], [Optional(1), nil, nil], [Optional(5), Optional(5), nil], [Optional(3), Optional(5), Optional(3)]]

filter

  • 컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출
// 기존의 방법
var filtered: [Int] = [Int]()
for number in numbers{
    if number % 2 == 0 {
        filtered.append(number)
    }
} // [0, 2, 4]

// filter 사용
let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in
    return number % 2 == 0
} // [0, 2, 4]

// 매개변수, 반환 타입, 반환 키워드 생략, 후행 클로저
let oddNumbers: [Int] = numbers.filter { $0 % 2 != 0 } [1, 3]

reduce

  • 컨테이너 내부의 콘텐츠를 하나로 통합
// 기존의 방법
let someNumbers: [Int] = [2, 8, 15]

var result: Int = 0
for number in someNumbers {
    result += number
} // 25

// reduce 사용
let sum: Int = someNumbers.reduce(0, {(first: Int, second: Int) -> Int in
    return first + second
})

// 초기값으로부터 모든 값을 빼는 방법
let subtract: Int = someNumbers.reduce(0, { $0 - $1 }) // -25

// 매개변수, 반환 타입, 반환 키워드 생략, 후행 클로저
let sumFromThree = someNumbers.reduce(3) { $0 + $1 }

Reference