고차함수의 코드를 이해하려면 클로저에 대해서 먼저 알아야 합니다. 아래의 제 포스팅이나 구글링을 통해 공부해보면 좋습니다!
map
map 함수는 컨테이너 내부의 기존 데이터를 변형하여 새로운 컨테이너를 생성합니다.
시간복잡도: O(n), n은 컨테이너의 길이
우리가 흔히 사용하는 자료구조인 Array, Set 그리고 Dictionary와 같은 자료구조들이 일종의 컨테이너라고 할 수 있습니다.
- map은 이렇게 사용합니다
let numbers: [Int] = [0, 1, 2, 3, 4]
var doubledNumbers: [Int]
// numbers의 각 요소를 2배하여 새로운 배열 반환
doubledNumbers = numbers.map({ (number: Int) -> Int in
return number * 2
})
print(doubledNumbers) // [0, 2, 4, 6, 8]
- 이렇게 축약도 가능합니다
let numbers: [Int] = [0, 1, 2, 3, 4]
var doubledNumbers: [Int]
// 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저
doubledNumbers = numbers.map { $0 * 2 }
print(doubledNumbers) // [0, 2, 4, 6, 8]
filter
filter 함수는 컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출합니다.
시간복잡도: O(n), n은 컨테이너의 길이
- filter는 이렇게 사용합니다
let numbers: [Int] = [0, 1, 2, 3, 4]
// numbers의 요소 중 짝수를 걸러내어 새로운 배열로 반환
// number % 2 == 0이 true이면 새로운 배열에 넣어준다
let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in
return number % 2 == 0
}
print(evenNumbers) // [0, 2, 4]
- 이렇게 축약도 가능합니다
let numbers: [Int] = [0, 1, 2, 3, 4]
// 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저
let oddNumbers: [Int] = numbers.filter {
$0 % 2 != 0
}
print(oddNumbers) // [1, 3]
reduce
reduce함수는 컨테이너 내부의 콘텐츠를 하나로 통합해줍니다.
시간복잡도: O(n), n은 컨테이너의 길이
-reduce는 이렇게 사용합니다
let someNumbers: [Int] = [2, 8, 15]
// 초깃값이 0이고 someNumbers 내부의 모든 값을 더합니다.
let sum: Int = someNumbers.reduce(0, { (first: Int, second: Int) -> Int in
print("\(first) + \(second)")
// first:0 + second:2 = 2
// first:2 + second:8 = 10
// first:10 + second:15 = 25
return first + second
})
print(sum) // 25
// 초깃값이 0이고 someNumbers 내부의 모든 값을 뺍니다.
var subtract: Int = someNumbers.reduce(0, { (first: Int, second: Int) -> Int in
print("\(first) - \(second)")
// 0-2
// -2-8
// -10-15
return first - second
})
print(subtract) // -25
// 배열 내 String값들을 이어 붙입니다
let arr = ["요소1", "요소2", "요소3", "요소4"]
print(arr.reduce(""){$0+$1}) // 요소1요소2요소3요소4
- 이렇게 축약도 가능합니다
let someNumbers: [Int] = [2, 8, 15]
// 초깃값이 3이고 someNumbers 내부의 모든 값을 더합니다.
let sumFromThree = someNumbers.reduce(3) { $0 + $1 }
print(sumFromThree) // 28
filter, map, reduce 활용 예시
var arr1 = ["요소1", "요소2", "요소3"]
let new_arr1: [String] = arr1.map{
if $0 != arr1.last {
return $0+","
}
return $0
}
print(new_arr1.reduce(""){$0+$1}) // 요소1, 요소2, 요소3
forEach
for-in 구문과 비슷하지만 forEach는 반복문이 아닙니다.
반복하고 싶은 구문을 forEach라는 함수의 파라미터로 "클로저"로 작성하여 넘겨주는 것.
때문에 반복문 안에서만 사용할 수 있는 continue, break을 사용할 수 없습니다. return의 경우에도 for-in에서는 return을 만나면 함수가 종료되지만 forEach에서는 for-in의 continue와 같이 동작합니다.
-forEach는 다음과 같이 사용합니다.
let numbers = [1,2,3,4,5]
numbers.forEach {
if $0 % 2 == 0{
return
}else{
print($0)
}
}
// 1,3,5
FlatMap
flatMap함수는 2차원 배열을 1차원 배열로 flat하게 만들어주고 싶을 때 사용합니다.
시간복잡도: O(n+m), n은 배열의 길이 m은 결과값의 길이
-flatMap은 이렇게 사용합니다.
let scores = [[5,2,7], [4,8], [9,1,3]]
let allScores = scores.flatMap { $0 } // [5, 2, 7, 4, 8, 9, 1, 3]
let passMarks = scores.flatMap { $0.filter { $0 > 5} } // [7, 8, 9]
CompactMap
compactMap함수는 1차원 배열에서 1. nil값을 제거하고 2.옵셔널 바인딩을 해줍니다.
시간복잡도: O(n+m), n은 배열의 길이 m은 결과값의 길이
원래는 flatMap에서 전부 다 하던 일들이지만 Swift 4.1부터 CompactMap이 등장하고 역할이 분배되었습니다.
-compactMap은 이렇게 사용합니다.
let array = [1,2,nil,10,123,nil,nil]
let cmapTest = array.compactMap{ $0 } //[1, 2, 10, 123]
그럼 이 코드는 어떻게 될까요?
let array: [[Int?]] = [[1, 2, 3], [nil, 5], [6, nil], [nil, nil]]
let cmapTest = array.compactMap{ $0 }
이 코드의 결과 값은 [[Optional(1), Optional(2), Optional(3)], [nil, Optional(5)], [Optional(6), nil], [nil, nil]] 입니다.
뭐야 nil값도 제거 못했고 옵셔널 바인딩도 못해줬네요? 바로 array가 2차원 배열이라서 그렇습니다. 얄짤없죠
그치만 2차원 배열에 있는 nil값을 없애는 방법이 없는것은 아닙니다. 우리는 flatMap을 배웠으니까요!
let array: [[Int?]] = [[1, 2, 3], [nil, 5], [6, nil], [nil, nil]]
let fmapcmapMap = array.flatMap { $0 }.compactMap{ $0 } //[1, 2, 3, 5, 6]
flatMap으로 먼저 1차원 배열을 만들어준다면 compactMap으로 nil 제거 및 옵셔널 바인딩을 해줄 수 있게됩니다!