Swift

[Swift] 맵, 필터, 리듀스

yevdev 2022. 3. 22. 14:21

1️⃣ 맵

자신을 호출할 때 매개변수로 전달된 함수를 실행하여 그 결과를 다시 반환해주는 함수

기존 데이터를 변형하는데 많이 사용

 

map 매서드를 클로저 표현을 이용하여 간략화하기

let numbers: [Int] = [0, 1, 2, 3, 4]

// 기본 클로저의 표현식 사용
var doubleNumbers = numbers.map({(number: Int) -> Int in
    return number * 2})

// 매개변수 및 반환 타입 생략
doubleNumbers = numbers.map({return $0 * 2})
print(doubleNumbers)

// 반환 키워드 생략
doubleNumbers = numbers.map({ $0 * 2})
print(doubleNumbers)

// 후행 클로저 사용
doubleNumbers = numbers.map { $0 * 2 }
print(doubleNumbers)

// [0, 2, 4, 6, 8]

 

클로저의 반복 사용 (재사용)

let evenNumbers: [Int] = [0,2,4,6,8]
let oddNumbers: [Int] = [1,3,5,7,9]
let multiplyTwo: (Int) -> Int = { $0 * 2 }

let doubleEvenNumbers = evenNumbers.map(multiplyTwo)
print(doubleEvenNumbers)

let doubleOddNumbers = oddNumbers.map(multiplyTwo)
print(doubleOddNumbers)

// [0, 4, 8, 12, 16]
// [2, 6, 10, 14, 18]

 

다양한 컨테이너 타입에서 맵의 활용

let alphabetDictionary: [String: String] = ["a": "A", "b": "B"]

// keys 설정 (1)
var keys: [String] = alphabetDictionary.map{ (tuple: (String, String)) ->
    String in
    return tuple.0
}
// keys 설정 (2)
keys = alphabetDictionary.map{ $0.0 }

let values: [String] = alphabetDictionary.map{ $0.1 }
print(keys) // ["a", "b"]
print(values)   // ["A", "B"]

var numberSet: Set<Int> = [1, 2, 3, 4, 5]
let resultSet = numberSet.map{ $0*2 }
print(resultSet)    // [2, 4, 6, 10, 8]

var optionalInt: Int? = 3
let resultInt: Int? = optionalInt.map{ $0*2 }
print(resultInt!)  // 6

let range: CountableClosedRange = (0...3)
let resultRange: [Int] = range.map{ $0*2 }
print(resultRange)  // [0, 2, 4, 6]

 

 

2️⃣ 필터

컨테이너 내부의 값을 걸러서 추출하는 역할을 하는 고차함수

 

필터 메서드의 사용

let numbers: [Int] = [0, 1, 2, 3, 4, 5]

let evenNumbers: [Int] = numbers.filter{ (number: Int) -> Bool in
    return number % 2 == 0
}
print(evenNumbers)

let oddNumbers: [Int] = numbers.filter{ (number: Int) -> Bool in
    return number % 2 == 1
}
print(oddNumbers)

// [0, 2, 4]
// [1, 3, 5]

 

맵과 필터 메서드의 연계 사용

let numbers: [Int] = [0, 1, 2, 3, 4, 5]
let mappedNumbers: [Int] = numbers.map{($0 + 3)}

let evenNumbers: [Int] = mappedNumbers.filter{ (number: Int) -> Bool in
    return number % 2 == 0
}
print(evenNumbers)

let oddNumbers: [Int] = mappedNumbers.filter{ (number: Int) -> Bool in
    return number % 2 == 1
}
print(oddNumbers)

// [4, 6, 8]
// [3, 5, 7]

 

 

3️⃣ 리듀스

컨테이너 내부의 콘텐츠를 하나로 합하는 기능을 실행하는 고차함수

 

1. 리듀스의 첫번째 형태 reduce(_:_:)

let numbers: [Int] = [1,2,3]

// 첫번째 형태인 reduce(_:_:) 메서드의 사용

// (1) 초깃값이 0이고 정수 배열의 모든 값을 더한다.
var sum: Int = numbers.reduce(0, {(result: Int, next: Int) -> Int in
    print("\(result) + \(next)")
    return result + next
})
print(sum)
// 0 + 1
// 1 + 2
// 3 + 3
// 6


// (2) 초깃값이 0이고 정수 배열의 모든 값을 뺀다.
var subtract: Int = numbers.reduce(0, {(result: Int, next: Int) -> Int in
    print("\(result) - \(next)")
    return result - next
})
print(subtract)
// 0-1
// -1-2
// -3-3
// -6


//  (3) 초깃값이 3이고 정수 배열의 모든 값을 더한다
var sumFromThree: Int = numbers.reduce(3) {
    print("\($0) + \($1)")
    return $0 + $1
}
print(sumFromThree)
// 3+1
// 4+2
// 6+3
// 9


//  (4) 초깃값이 3이고 정수 배열의 모든 값을 뺀다
var subtractFromThree: Int = numbers.reduce(3){
    print("\($0) - \($1)")
    return $0 - $1
}
print(subtractFromThree)
// 3-1
// 2-2
// 0-3
// -3


// (5) 문자열 배열을 reduce(_:_:) 메서드를 이용해 연결시킨다.
let names: [String] = ["Chope", "Jay", "Joker", "Nova"]

let reduceNames: String = names.reduce("yejin's friend: ") {
    return $0 + ", " + $1
}
print(reduceNames)
//yejin's friend: , Chope, Jay, Joker, Nova

2. 리듀스의 두번째 형태 reduce(into:_:)

// 두번째 형태인 reduce(into:_:) 메서드의 사용

// (1) 초깃값이 0이고 정수 배열의 모든 값을 더한다.
// 	- 첫번째 리듀스 형태와 달리 클로저의 값을 반환하지 않고 내부에서 직접 이전 값을 변경한다는 점이 다름
sum = numbers.reduce(into: 0, {(result: inout Int, next: Int) in
    print("\(result) + \(next)")
    result += next
})
print(sum)
// 0+1
// 1+2
// 3+3
// 6

// (2) 초깃값이 3이고 정수 배열의 모든 값을 뺀다.
// 	- 첫번째 리듀스 형태와 달리 클로저의 값을 반환하지 않고 내부에서 직접 이전 값을 변경한다는 점이 다름
subtractFromThree = numbers.reduce(into: 3, {
    print("\($0) - \($1)")
    $0 - $1
})
print(subtractFromThree)
//3 - 1
//3 - 2
//3 - 3
//3

 

맵, 필터, 리듀스의 연계 사용

let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7]

// 짝수를 걸러내어 각 값에 3을 곱한 후 모든 값을 더한다.
var result: Int = numbers.filter{ $0.isMultiple(of: 2) }.map{ $0 * 3 }.reduce(0){ $0 + $1 }
print(result)	// 36	

// for-in 구문 사용 시
result = 0
for number in numbers{
    guard number.isMultiple(of: 2) else {
        continue
    }
    result += number * 3
}
print(result)	// 36

 

 

4️⃣ 맵, 필터, 리듀스의 활용

enum Gender {
    case male, female, unknown
}

struct Friend {
    let name: String
    let gender: Gender
    let location: String
    var age: UInt
}

var friends: [Friend] = [Friend]()

friends.append(Friend(name: "yejin", gender: .female, location: "서울", age: 22))
friends.append(Friend(name: "jaeyong", gender: .male, location: "미래관", age: 24))
friends.append(Friend(name: "jonghwan", gender: .male, location: "경기", age: 28))
friends.append(Friend(name: "inhye", gender: .female, location: "대전", age: 22))

// 서울 외의 지역에 거주중인 25살 이상의 친구 찾기
// 작년 자료이므로 한살 더 먹게 하기!
var result: [Friend] = friends.map{ Friend(name: $0.name, gender: $0.gender, location: $0.location, age: $0.age+1)}

result = result.filter{ $0.location != "서울" && $0.age >= 25 }

let resultString: String = result.reduce(" 서울 외의 지역에 거주하며 25세 이상인 친구 "){
    "\($0)\n\($1.name) \($1.age) \($1.location) \($1.gender)"
}

print(resultString)

// 서울 외의 지역에 거주하며 25세 이상인 친구 
//jonghwan 29 경기 male

1. 으로 나이를 한살씩 더해 새로운 Friend 배열을 생성

2. 필터로 서울에 사는 친구들과 25세 미만인 친구들을 걸러낸 후

3. 리듀스로 필터링한 후 변형된 자료를 원하는 모양으로 합쳐서 출력

 


Reference

Swift 스위프트 프로그래밍 - 야곰