---
# System prepended metadata

title: '고차함수, map, compactMap, flatMap, reduce'

---

# 고차함수, map, compactMap, flatMap, reduce

## 고차함수
> 정의: 함수를 매개변수로 사용하거나 함수실행의 결과를 함수로 반환하는 함수

내 이해로는 **함수자체를 값처럼 취급**해 함수의 인자(리턴값, 매개변수)로 사용한다.

<img width = 300, src = "https://i.imgur.com/0XI7xaB.png">


**가능한이유?**
스위프트의 함수(클로저)는 **일급시민**이기 때문에 함수의 전달인자로 전달할 수 있으며 함수의 결과값으로 반환할 수 있습니다.

💡 **1급시민이란?** (First-class citizen)
 아래 세가지가 가능한 것.
- ✅ 변수에 저장이 가능한가 ?
- ✅ 매개변수로 전달이 가능한가 ?
- ✅ 리턴값으로 사용가능한가?
    - ✔️ 변수에 함수를 저장하는 예시
        ```swift=
        func hello() -> String {
            return "hello"
        }

        var A = hello()
        print(A) // "hello"
        ```
    - ✔️ 함수를 매개변수로 전달하는 예시
        ```swift=
        func Say(something: String) -> String {
            return something
        }

        print(Say(something: hello())) // "hello"
        ```
    - ✔️ 결과를 함수로 리턴하는 예시
        ```swift=
        func sayFunction() -> String {
            return hello()
        }

        print(sayFunction()) //"hello"
        ``` 
- 고차함수의 예시        
    - map, compactMap, FlatMap,
    - Reduce
    - forEach

---

## map
- 컨테이너 내부의 데이터를 변형하여 새로운 컨테이너를 생성한다.
    - 스위프트의 컨테이너란 `array, set, dictionary` 등을 의미한다.

![](https://i.imgur.com/jhF7wbV.png)

```swift
//Array 활용
let array = [1, 2, 3, 4, 5]
print(array.map { $0 * 2 }) 
print(array.map { "Number: \($0)" })

//Set 활용
let set: Set<Int> = [1,2,3,4,4]
print(set.map { $0 * 3 })

//Dictionary 활용
let menus: [String: Int] = ["아메리카노": 4000, "라떼": 4500, "바닐라라떼": 5000, "딸기라떼": 5000]
let menuInfo = menus.map { "\($0)의 가격은 \($1)원 입니다." }
menuInfo.forEach { print($0) }

//String 활용
let letters = "abcdefghijklmnop"
let upperCased = letters.map { $0.uppercased() }
print(upperCased)
print(upperCased.joined())

//숫자를 영단어로 바꾸는 예시
let digitNames = [
    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [235, 38, 827, 47, 60]

print(numbers.map { number -> String in
    var number = number
    var result = ""
    
    while number > 0 {
        result = digitNames[number % 10]! + result
        number /= 10
    }
    
    return result
})
```


---

## Reduce

- 내부의 값을 결합하여 새로운 값을 만들어 준다.
- 스위프트 GitHub 상 공개 되어 있는 Reduce의 구조는 다음과 같다.
    ```swift
    @inlinable
    public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (_ partialResult: Result, Element) throws -> Result) rethrows -> Result {
      var accumulator = initialResult
      for element in self {
        accumulator = try nextPartialResult(accumulator, element)
      }
      return accumulator
    }
    ```
    - 위와 같은 주는 기본 값을 가지고, 두개의 인자를 받아서 Result 타입으로 변환하여 반환하는 구조를 가지고 있다.
    - 위와 같은 구조에서 일반적으로 알지 못하는 타입을 아래와 같이 바꿔서 생각해볼 수 있다.
    
    ```swift
    extension Array where Element == Int {
        func myReduce<Int>(_ initialResult: Int, _ nextPartialResult: (Int, Int) throws -> Int) rethrows -> Int {
            var result: Int = initialResult

            for value in self {
                if let value = value as? Int {
                    result = try nextPartialResult(result, value)
                }
            }

            return result
        }
    }
    ```
    - 다음과 같은 함수로 아래의 값을 실행하게 되면 모두 동일하게 1~10까지 더한 수가 나오는 것을 확인 할 수 있다.
    ```swift
    let nums: [Int] = Array(1...10) // 1 ~ 10 모두 더한 값이

    let sum = nums.myReduce(0) { partialResult, value in
        return partialResult + value
    }

    let sum2 = nums.myReduce(0) { return $0 + $1 } //클로저 축약
    let sum3 = nums.myReduce(0) { $0 + $1 } // return 구문 축약
    let sum4 = nums.myReduce(0, +) // + 함수로 클로저 타입 충족

    print(sum) // 55
    print(sum2) // 55
    print(sum3) // 55
    print(sum4) // 55

    ```
---

## compactMap 

- map + 옵셔널 제거
- 기존 배열 등의 각 아이템을 새롭게 매핑해서(매핑방식은 클로저가 제공)
  변형하되, 옵셔널 요소는 제거하고, 새로운 배열을 리턴
- 옵셔널 바인딩의 기능까지 내장
```swift
let stringArray: [String?] = ["A", nil, "B", nil, "C"]

var newStringArray = stringArray.compactMap { $0 }
print(newStringArray)
//["A", "B", "C"]

let numbers = [-2, -1, 0, 1, 2]

var positiveNumbers = numbers.compactMap { $0 >= 0 ? $0 : nil }

print(positiveNumbers)
//[0, 1, 2]
```

---

## flatMap

- 중첩된 배열의 각 배열을 새롭게 매핑한다.
- 내부 중첩된 배열을 제거하고 리턴


```swift
var nestedArray1: [[Int]] = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 3]]

print(nestedArray1.flatMap { $0 })
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 3]

var nestedArray2: [[Int?]] = [[1, 2, 3], [nil], [4, 5, 6], [7, 8, 9], [1, nil, 3]]
print(nestedArray2.flatMap { $0 })
// [Optional(1), Optional(2), Optional(3), nil, Optional(4), 
//  Optional(5), Optional(6), Optional(7), Optional(8), 
//  Optional(9), Optional(1), nil, Optional(3)]

var nestedArray3: [[Int?]?] = [[1, 2, 3], nil ,[nil] , [4, 5, 6], [7, 8, 9], [1, nil, 3]]
print(nestedArray3.flatMap { $0 })
// [[Optional(1), Optional(2), Optional(3)], [nil], 
// [Optional(4), Optional(5), Optional(6)], 
// [Optional(7), Optional(8), Optional(9)], 
// [Optional(1), nil, Optional(3)]]

print(nestedArray3.flatMap { $0 }.flatMap { $0 })
// [Optional(1), Optional(2), Optional(3), nil, Optional(4), 
// Optional(5), Optional(6), Optional(7), Optional(8), 
// Optional(9), Optional(1), nil, Optional(3)]

var newNnestedArray = [[[1,2,3], [4,5,6], [7, 8, 9]], [[10, 11], [12, 13, 14]]]

var numbersArray = newNnestedArray
                            .flatMap { $0 }
                            .flatMap { $0 }

print(numbersArray)
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
```



---
# 고차함수의 사용의 장단점

1. 장점
 - 간결하다
 - let으로 선언가능하다.
     - 반복/조건문 사용시 변수(var)로 선언하여 사이드 이펙트가 발생할 수 있지만 고차함수는 상수 let으로 함수 내부에서 바로 표현해주며 할당할 수 있어 한번에 표현이 가능해서 사이드 이펙트 발생을 막아줍니다.
 - 메서드체이닝으로 연결하여 사용할 수 있다.
 - 반복/조건문을 한 단어로 표현할 수 있어 코드를 극적으로 단순화 할 수 있다.


2. 단점
 - 없는게 단점...그저 완벽
 - 너무나 완벽한 고차 함수~~
 - 그나마 단점..가독성..
 - 탈출 못함...? 요건 좀 안좋은듯...
 - hello world
 
 
 ## 오토 레이아웃 공유
 
 --- 

 
 # (과제) 오토레이아웃 제약 표현해보기

<img width = 400, src = "https://i.imgur.com/Udk1UeM.png"> 


#### 써니쿠키

- 1. 너비가 같은 두 뷰.

greenView.Top = safeArea.Top + 10 
greenView.Bottom = safeArea.Bottom - 10
greenView.leading = safeArea.leading + 10

yellowView.Top = safeArea.Top + 10 
yellowView.Bottom = safeArea.Bottom - 10
yellowView.tailing = safeArea.tailing - 10

greenView.width = yellowView.width
greenView.tailing = yellowView.laeding - 10

- 2. 너비가 1:2 인 뷰

greenView.Top = safeArea.Top + 10 
greenView.Bottom = safeArea.Bottom - 10
greenView.leading = safeArea.leading + 10

yellowView.Top = safeArea.Top + 10 
yellowView.Bottom = safeArea.Bottom - 10
yellowView.tailing = safeArea.tailing - 10

greenView.width = 0.5 X yellowView.width
greenView.tailing = yellowView.laeding - 10


 #### 인호
 **1:1인 경우**
 greenView.Top = safeArea.Top + 10
 greenView.Leading = safeArea.Leading + 10
 safeArea.Bottom = greenView.Bottom + 10
 orangeView.leading = greenView.leading + 10
 
 orangeView.width = 1 * greenView.width
 
 orangeView.Top = greenView.Top
 orangeView.Bottom = greenView.Bottom
 safeArea.Trailing = orangeView.Trailing + 10
 
 **(그린: 오렌지)1:2인 경우**
  greenView.Top = safeArea.Top + 10
 greenView.Leading = safeArea.Leading + 10
 safeArea.Bottom = greenView.Bottom + 10
 orangeView.leading = greenView.leading + 10
 
 orangeView.width = 2 * greenView.width
 
 orangeView.Top = greenView.Top
 orangeView.Bottom = greenView.Bottom
 safeArea.Trailing = orangeView.Trailing + 10
 
 #### Kyo
 greenView.Top = 1 * SafeArea.Top + 10
 greenView.Bottom = 1 * SafeArea.Bottom
 greenView.Leading = 1 * SafeArea.Leading + 10
 greenView.Trailing = 1 * OrangeView.Leading - 10
 
 greenView.Width = 1 * OrangeView
 greenView.Height = 1 * SafeArea.Height
 OrangeView.Height = 1 * SafeArea.Height
 OrangeView.Leading = 1 * greanView.Trailing + 10
 OrangeView.Trailing = 1 * SafeArea.Trailing - 10
 OrangeView.Top = 1 * SafeArea.Top + 10
 OrangeView.Bottom = 1 * SafeArea.Bottom
 
#### 미니

##### 1:1
greenView.top = safeArea.top + 10
greenView.bottom = safeArea.bottom + 10
greenView.leading = safeArea.leading + 10
greenView.trailing = yellowView.leading - 5

yellowView.top = safeArea.top + 10
yellowView.trailing = safeArea.trailing + 10
yellowView.bottom = safeArea.bottom + 10
yellowView.leading = greenView.trailing - 5

greenView.width = yellowView.width


##### 1:2
greenView.top = safeArea.top + 10
greenView.bottom = safeArea.bottom - 10
greenView.leading = safeArea.leading + 10
greenView.trailing = yellowView.leading - 5

yellowView.top = safeArea.top + 10
yellowView.trailing = safeArea.trailing + 10
yellowView.bottom = safeArea.bottom - 10
yellowView.leading = greenView.trailing - 5

greenView.width = yellowView.width * 0.5