[iOS-Swift] 고차함수 Map 활용하기

 

TIL 19일 차 - 고차함수 Map, compactMap, flatMap 활용하기

<Map>

1. 모든 원소에 동일한 연산 적용하기

// 모든 숫자 2배로 만들기 
let nums = [1, 2, 3, 4]

let doubled = nums.map { $0 * 2 } // 각 원소에 같은 연산을 적용
// [2, 4, 6, 8] 출력

 

 

2. 타입 변환 (ex. Int -> String)

let nums = [1, 2, 3]

let Strings = num.map { String($0) }
// ["1", "2", "3"] 출력

 

 

3. 문자열 배열 가공 (전부 대문자로 바꾸기)

let names = ["iOS", "Swift", "Apple"]

let upper = names.map { $0.uppercased() }
// ["IOS", "SWIFT", "APPLE"] 출력

 

 

4. 모델 -> 필요한 값만 추출

// 객체 배열 -> 특정 프로퍼티 배열
struct User {
	let name: String
    let age: Int
}

let users = [
	User(name: "A", age: 20)
    User(name: "B", age: 25)
]

let names = users.map { $0.name }
// ["A", "B"]

 

 

5. Optional과 map

let number: Int? = 3

let result = number.map { $0 * 2 }
// Optional(6)    // 옵셔널에서도 map 가능

 

 

5. Range를 Array로 변경

// 빈 배열, 반복문 필요 없이 한줄로 생성 가능
let arr = (0...9).map { $0 }

// 0부터 9까지 들어있는 배열
// arr == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// 사실은 let arr = Array(0...9)로도 쓸 수 있음

 

 

<compactMap>

compactMap은 Map과 다른 점이 무엇일까?

Map: 원소의 개수가 변하지 않음, nil도 결과에 포함시킴

compactMap: nil을 제거하고 값만 모음

 

1. nil값을 제거

// Map에서의 Optional

let strings = ["1", "2", "three", "4"]

let mapped = string.map { Int($0) }

// [Int?, Int?, Int?, Int?]
// [1, 2, nil, 4]
// compactMap에서의 optional
// 위와 같은 예제일때

let compacted = string.compactMap { Int($0) }
// [1, 2, 4] // nil값 제거 됨

 

따라서 map을 써야할때: 

- 결과 개수가 무조건 같아야 할 때

- nil도 의미 있는 값일 때

- 단순 변형

 

compactMap을 써야할 때:

- 변환 중 실패할 수 있을때 (앱 폭발 방지)

- nil을 버리고 싶을때 

- Optional 제거가 목적일 때

 

 

2. 문자열 -> 숫자 변환 (실무/코테에서 자주 쓰는 패턴)

let inputs = ["10", "20", "x", "30"]

let numbers = input.compactMap { Int($0) }
// [10, 20, 30]

 

 

3. guard + compactMap으로 숫자 입력 검증하기

let inputs = ["1", "2", "x"]

let nums = inputs.compactMap(Int.init)

guard nums.count == inputs.count else {
	print("숫자가 아닌 입력이 있습니다.")
    return
}

 

 

4. 입력한 수를 각각 한개씩 쪼개 배열로 반환하기 

    // MARK: - 입력한 수를 숫자 각 한개씩으로 배열로 쪼개는 함수
    private func splitNum(_ num: Int) -> [Int] {
        let result = String(num).compactMap{ Int(String($0))}
            return result
        }

먼저 입력한 세자리수 num이 String(num)으로 들어와 예) 907 -> "907" 문자열로 변환된다.

그런 다음은 "907".compactMap{ Int(String($0)) }인데 문자열은 character단위로 ["9", "0", "7"]로 순회하므로

String -> Int순으로 만들어주고 숫자면 Int? 값으로 반환되고 숫자가 아니면 nil값이 반환된다(compactMap에선 삭제됨) 

 

이 코드의 장점은 자리수가 자동으로 처리되어 유동적으로 사용할 수 있다.

 

 

<flatMap>

flatMap은 옵셔널 안에 옵셔널이 들어있는 상태(??)를 한 겹(?)으로 펴는 기능도 한다.

guard let inputNumber = readLine().flatMap(Int.init)

1) 먼저 위의 코드의 readLine()은 string? 값을 return 한다.

 

2) 그다음 Int.init은 입력받은 문자열을 숫자로 변환하려 하므로 Int? 값을 반환한다.

 

3) 만약 flatMap이 아닌 map을 사용한다면 Int?? 를 반환하게 된다.

 

4) 대신 flatMap을 사용하면 Int? 값을 return 해주어 구조를 단순하게 정리해 주게 된다.

 

5) guard let inputNumber =.. 단계에서 옵셔널을 해제한다.