Swift - Collection 타입의 도구들: map, filter, reduce, zip
기초적이지만 알아두면 나쁠 건 없는 스위프트(Swift)의 Collection Type (주로 Array) 활용 메소드 map, filter 그리고 reduce를 간단히 복습해보는 글. 추가로 zip에 관해서도 정리해 보자.
이미 애플의 스위프트 공식 책(?)에도 간단한 예제가 나와있기 때문에 이해했다면 굳이 볼 필요는 없는 내용이다.
이 메소드는 특정 Array나 Dictionary를 기준으로 다른 Array를 만들어 낼 때 사용하는 메소드다. Array에서 이용하는 경우 map의 원형은 아래와 같다.
단순하게 생각해보면, map은 배열을 검토해서 정리할 때 활용된다고 볼 수 있다.
추가로, Dictionary 에서 map을 사용할 때는 아래와 같은 식이다.
Dictionary를 필터링 하는 경우도 비슷하긴 한데 뭔가 미묘하게 다르다.
이 AnyObject의 경우 'is' 라는 오퍼레이터를 이용해 타입을 확인할 수가 있다. 따라서 아래 예제와 같이 이용이 가능하다.
그런데 reduce의 경우는 좀 애매하다. 애초에 목적이 배열의 데이터를 이용해 연산하는 것이다 보니 AnyObject로 활용하기에는 좀 귀찮다. 초기값과 combine 클로져의 리턴값의 타입을 맞춰야 한다는 것을 생각해보자. 하기싫어진다. :-)
이상 Array의 세 가지 도구를 살펴봤다.
참고로 Python이나 Lisp 같은 언어를 이용해 봤다면 메소드 이름 만으로도 뭘 하는 것인지는 파악이 가능하리라 생각된다.
[관련글] Swift - flatMap 넌 도데체 뭐냐
[관련글] 스위프트(Swift) 가이드
이미 애플의 스위프트 공식 책(?)에도 간단한 예제가 나와있기 때문에 이해했다면 굳이 볼 필요는 없는 내용이다.
map
map 이라고 해서 '지도' 라는 의미는 아니다. 오히려 이름 그대로 '매핑(Mapping)' 이라는 단어가 더 와닿을지도 모르겠다.이 메소드는 특정 Array나 Dictionary를 기준으로 다른 Array를 만들어 낼 때 사용하는 메소드다. Array에서 이용하는 경우 map의 원형은 아래와 같다.
Array.map(transform: T -> U)위의 원형이라고 적은것에서 두 가지 타입 T와 U표현을 잘 보자. 타입이 유동적이다. transform 으로 넘기는 것은 클로져(Closure)인데 T는 원래 Array의 타입이고 U는 새롭게 만들 Array의 아이템 타입이다.
let values = [1, 2, 3, 4, 5]
let valueStrings = values.map({ (v: Int) -> (String) in
return "\(v)"
})
println(valueStrings)
// 콘솔에 ["1", "2", "3", "4", "5"] 가 출력된다.
정수형 아이템들을 가지는 배열(Array) values를 만들었다. 그리고 이 values를 map을 이용해 정수형이 아닌 문자열형(String) Array를 만들어냈다.단순하게 생각해보면, map은 배열을 검토해서 정리할 때 활용된다고 볼 수 있다.
추가로, Dictionary 에서 map을 사용할 때는 아래와 같은 식이다.
Dictionary.map(transform: (KEY, VALUE) -> U)인자가 키와 값 두 가지로 나눠져서 넘어간다는 것을 제외하면 거의 동일하다.
let dict: [String: Int] = [ "a": 1, "b": 2 ] dict.map { (key, value) in return "\(key)=\(value)" }앞서 본 예제가 이해되었다면 이 예제도 이해가 될 것이다.
filter
필터는 딱 와닿는 이름이다. 배열(Array)에서 특정 아이템을 걸러낼 때 이용한다.Array.filter(includeElement: T -> Bool)역시나 배열의 아이템 타입에 맞게 클로져를 넘겨주어야 한다. 그런데 이번에는 Bool 타입을 리턴하는 클로져다. 대충 상상해보면 아마도 false를 리턴하는 아이템은 걸러내는 형식인 것 같다.
let evenValues = values.filter({ (v: Int) -> (Bool) in
if v % 2 == 0 { return true }
return false
})
println(evenValues)
// 콘솔에 [2, 4] 가 출력된다.
예상이 맞다. filter는 배열에서 필요없는 아이템을 걸러낸 배열을 리턴하는 메소드다.Dictionary를 필터링 하는 경우도 비슷하긴 한데 뭔가 미묘하게 다르다.
let dict: [String: Int] = [ "a": 1, "b": 2 ] dict.filter { (key, value) in return (value == 2) }이 경우 value가 2인 두 번째 아이템이 넘어와야 할 것 같은데, (String, Int) 형식의 튜플 형식의 Array로 넘어온다. 이런 경우에는 주의해서 사용하자.
reduce
reduce의 경우는 좀 다르다. 이 녀석은 combine 이라는 표현이 더 잘 어울리는데, 하는 일은 배열의 모든 아이템을 꺼내서 하나로 합치는 형태이기 때문이다.Array.reduce(initial: U, combine: (U, T) -> U)initial 이라는 것은 초기값이다. 즉 initial 부터 시작해서 combine 클로져를 통해 연산을 한다.
let sumValues = values.reduce(0, combine: { (v1: Int, v2: Int) -> (Int) in
return v1 + v2
})
println(sumValues)
// 콘솔에 15 가 출력된다.
위 코드에서 reduce의 동작 방식을 유추해보자.- initial 값인 0 부터 시작
- 0과 value[0] 아이템을 꺼내서 연산
- 앞에서 연산된 결과와 values[1] 아이템을 꺼내서 연산
- 앞에서 연산된 결과와 values[2] 아이템을 꺼내서 연산
- ...
- 앞에서 연산된 결과와 values의 마지막 아이템을 꺼내서 연산
zip
zip은 두 리스트를 짝지어서 합쳐준다.let names = [ "Kim", "Park", "Lee" ] let scores = [ 90, 95, 80 ] let scoreBoard = zip(names, scores) // Zip2Sequence<Array<String>, Array<Int>>결과물은 Zip2Sequence 타입이라는 특이한 형식이 넘어오는데, 이 녀석은 for 루프로 이터레이션 할 때 유용하다.
for (name, score) in scoreBoard { print("\(name) = \(score)") }결과물을 예상해보자.
AnyObject와의 연동
싫은 좋든 간에, 스위프트로 코딩을 하더라도 Objective-C로 구현된 NSArray를 함께 사용하게 될 가능성이 높다. 특히 UIKit이나 Cocoa를 이용한다면 말이다. 그렇다면 Array의 타입도 AnyObject 형식으로 자주 사용하게 된다.이 AnyObject의 경우 'is' 라는 오퍼레이터를 이용해 타입을 확인할 수가 있다. 따라서 아래 예제와 같이 이용이 가능하다.
let objectList: Array = [10, "이십", 100, "이백"]
// 정수만 걸러내기 ------------------------------
let objectIntList = objectList.filter({ (input: AnyObject) -> (Bool) in
return input is Int
})
println(objectIntList)
// 콘솔에 [10, 100] 이 출력된다.
// 문자열만 걸러내기 -----------------------------
let objectStringOnlyList = objectList.filter({ (input: AnyObject) -> (Bool) in
return input is String
})
println(objectStringOnlyList)
// 콘솔에 ["이십", "이백"] 이 출력된다.
// 문자열 배열 구하기 ----------------------------
let objectStringList = objectList.map({ (input: AnyObject) -> (String) in
if input is String {
return input as String
} else {
return "\(input)"
}
})
println(objectStringList)
// 콘솔에 ["10", "이십", "100", "이백"] 이 출력된다.
AnyObject 타입의 아이템을 가지는 Array를 map과 filter로 활용하는 예제이다. 앞서 이야기 한 대로 'is' 라는 커맨드를 활용하고 있다. (사실 마지막 예제는 그냥 몽땅 마지막 리턴문으로 처리해 버리면 되는데 그냥 예제상 이렇게 했다. 오해하지 말자 -_-)그런데 reduce의 경우는 좀 애매하다. 애초에 목적이 배열의 데이터를 이용해 연산하는 것이다 보니 AnyObject로 활용하기에는 좀 귀찮다. 초기값과 combine 클로져의 리턴값의 타입을 맞춰야 한다는 것을 생각해보자. 하기싫어진다. :-)
이상 Array의 세 가지 도구를 살펴봤다.
참고로 Python이나 Lisp 같은 언어를 이용해 봤다면 메소드 이름 만으로도 뭘 하는 것인지는 파악이 가능하리라 생각된다.
flatMap
Swift 2.0에 추가된 map과 비슷한 녀석이지만 다른 녀석이다. 아래 글을 참고하자.[관련글] Swift - flatMap 넌 도데체 뭐냐
[관련글] 스위프트(Swift) 가이드
댓글