map
은 자신을 호출할 때 매개변수로 전달된 함수를 실행하여 그 결괏값을 다시 반환해주는 함수이다.map
을 사용하기 위해서는 Swift의 collection
, sequence
프로토콜을 따르면 가능하다. 따라서 Array
, Dictionary
,Set
,optional
등에서 사용이 가능map
을 사용하여도 기존의 컨테이너의 값은 변경되지 않고 새로운 컨테이너가 생성되어 map은 기존 데이터를 변형하는데 많이 사용된다.map
은 다른 함수의 형태로 입력을 받는다.또 , 다중 스레드 환경일 때 대상 컨테이너의 값이 스레드에서 변경되는 시점에 다른 스레드에서도 동시에 값이 변경되려고 할 때 예측하지 못한 결과가 발생하는 부작용을 방지한다.
let item = ["가방", "책", "블로그", "지갑"]
let first = item.map{ (name) in "서근의 " + name }
print(first)
let second = item.map({(name) in "서근의 " + name})
print(second)
let third = item.map {"서근의 " + $0}
print(third)
이것을 클로저로 바꾸면 아래와 같다.
let item = ["가방", "책", "블로그", "지갑"]
func addName(name: String) -> String {
return "서근의 " + name
}
item.map(addName)
for-in
구문과 map
메서드 사용을 비교해보자면 아래와 같다.
let numbers: [Int] = [0, 1, 2, 3, 4]
var doubledNumbers: [Int] = [Int]()
var string: [String] = [String]()
for number in numbers {
doubledNumbers.append(number * 2)
string.append("\(number)")
}
print(doubledNumbers) //[0, 2, 4, 6, 8]
print(string) //[0, 2, 4, 6, 8]
//map 메서드
doubledNumbers = numbers.map({ (number: Int) -> Int in
return number * 2 //[0, 2, 4, 6, 8]
})
string = numbers.map({ (number: Int) -> String in
return "\(number)" //["0", "1", "2", "3", "4"]
})
map
메서드를 사용하면 for-in
구문을 사용한 것보다 간단하고 편하게 연산을 실행할 수 있다. 또, map
메서드를 사용하면 for-in
구문을 사용하기 위해 빈 배열을 생성할 필요도, append
연산을 실행할 시간도 필요가 없어진다!!
위 코드에서 사용된 map
메서드를 클로저 표현으로 요약할 수 있다.
let numbers: [Int] = [0, 1, 2, 3, 4]
//map 메서드
doubledNumbers = numbers.map({ (number: Int) -> Int in
return number * 2 //[0, 2, 4, 6, 8]
})
string = numbers.map({ (number: Int) -> String in
return "\(number)" //["0", "1", "2", "3", "4"]
})
//유형 추론으로 요약 가능
doubledNumbers = numbers.map({ (number) in
return number * 2 //[0, 2, 4, 6, 8]
})
string = numbers.map({ (number) in
return "\(number)" //["0", "1", "2", "3", "4"]
})
//매개변수 및 반환 타입 생략
doubledNumbers = numbers.map({return $0 * 2})
string = numbers.map({return "\($0)"})
//반환 키워드 생략
doubledNumbers = numbers.map({$0 * 2})
string = numbers.map({"\($0)"})
//후행 클로저로 요약 가능
doubledNumbers = numbers.map { $0 * 2 }
string = numbers.map { "\($0)"}
코드의 재사용 측면에 대해 알아보자면 만약 같은 기능을 여러번 사용해야 한다면 하나의 클로저를 여러 map
메서드에서 사용하는 것이 좋다!
let evenNumbers: [Int] = [0, 2, 4, 6, 8, 10]
let oddNumbers: [Int] = [0, 1, 3, 5, 7, 9]
let multiplyTwo: (Int) -> Int = { $0 * 2 }
let doubledEvenNumbers = evenNumbers.map(multiplyTwo)
//[0, 4, 8, 12, 16, 20]
let doubledOddNumbers = oddNumbers.map(multiplyTwo)
//[0, 2, 6, 10, 14, 18]
filter
는 내부 값을 걸러서 추출하는 역할을 한다.map
과 동일하게 새로운 컨테이너에 걸러진 값을 담아 반환한다.map
은 기존의 요소를 변경한 값을 반환했다면, filter
는 기준을 가지고 기준에 맞는 값들을 반환해준다.filter
함수의 매개변수로 전달되는 함수 반환 타입은 Bool
이다.true
, 그게 아니라면 false
반환let number = [1, 2, 3, 4, 5]
print(number.filter {$0 > 3}) //4, 5
//필터 조건이 맞다면 map조건을 실행
let filterAndMap = [1, 2, 3, 4, 5].filter{$0 > 3}.map{$0 * 10}
print(filterAndMap) //40, 50
이런 식으로 filter 를 사용하여 필요 없는 요소들을 삭제하고 필요한 요소들만 가지고 연산을 하는 게 가능하다 !!
let numbers: [Int] = [0, 1, 2, 3, 4, 5]
var evenNumber: [Int] = numbers.filter { (number: Int) -> Bool in
return number % 2 == 0
}
print(evenNumber) // [0, 2, 4]
let oddNumbers: [Int] = numbers.filter { $0 % 2 == 1 }
print(oddNumbers) // [1, 3, 5]
첫 번째, 클로저가 각 요소를 전달받아 연산한 후 값을 다음 클로저 실행을 위해 반환하며 컨테이너를 순환하는 형태. initialResult
라는 이름의 매개변수로 전달되는 값을 통해 초깃값을 지정하고, nextPartialResult
매개변수로 클로저를 전달받음.
두 번째 , 컨테이너를 순환하며 클로저가 실행되지만 클로저가 따로 결괏값을 반환하지 않는 형태. 대신 inout
매개변수를 사용하여 초깃값에 직접 연산을 실행함.
let number = [1, 2, 3, 4, 5]
let sum1 = number.reduce(0) { (result:Int, element: Int) -> Int in return result + element }
print(sum1) //15
//추론으로 생략 가능
let sum2 = number.reduce(0) { (result, element) in result + element }
print(sum2) //15
let sum3 = number.reduce(0) {$0 + $1}
print(sum3) //15
let sum4 = number.reduce(1, +)
print(sum4) //16
/*
reduce 초기값이 0이기 때문에 0 + 1 부터 시작하여 마지막 값을 결괏값으로 보여준다.
0 + 1
1 + 2
3 + 3
6 + 4
10 + 5
결괏값 = 15
*/