본문 바로가기

Swift/Deep dive!!

[Swift] where keyword. 언제 사용할 수 있는지 탐구하기!!

안녕하세요. 언젠가 한 번 정리하려고 했던 where키워드를 복습할 겸 정리하려고 합니다.

 

 

where 키워드를 사용하면 정말 편합니다. Protocol extension에선 특정 프로토콜을 사용하기 위한 준수 조건을 지정하거나, array's extension에서 특정 함수를 추가했을 때 해당 함수를 사용할 수 있는 Element를 한정적으로 제한할 때, 루프, 제너릭, switch case, do-catch, guard 등 다양한 경우에 where 키워드를 사용할 수 있습니다. 그리고 특정 조건에 부합하는 filter 된 값을 받을 수 있습니다. 쉽게 생각하면 if의 조건을 where의 절에 붙인다는 느낌입니다,,

 

즉, where 키워드를 통해서 조건을 추가합니다. 특정 타입으로 제한하거나, 특정 조건에 맞는 데이터만 filter. 이런 경우에 유용하게 사용됩니다. 사전에 where 조건을 추가해서 조건에 일치하는 데이터만 다룰 수 있다는게 핵심인 것 같습니다.

 

1. Using where in for loop

let numbers = Array(0...1000)

for num in numbers {
    if num <= 101 {
        print("\(num)")
    }
}

위의 코드는 if 구문을 통해 101 이하의 숫자만 출력합니다. 이 경우 단점은 배열의 값은 순차적인 특징이 있습니다. 루프에서 if는 102 이상의 원소들은 더 이상 출력하지 않습니다. 그럼에도 루프는 num == 1000까지 반복을 합니다. 비효율적입니다.

 

for num in numbers where num <= 101 {
    print("\(num)")
}

for loop line 옆에 where 키워드를 사용해 Element에 특정 조건을 추가하게 된다면 for loop는 num == 101까지만 실행됩니다. 그리고  loop가 종료됩니다. 

 

Define model !!

여러 학생들 중 "A"형 혈액형인 학생들만 이름을 출력한다면. if문, filter operator를 통해 특정 값만 필터링할 수 있습니다.

struct Student {
    let name: String
    let bloodType: String
}
let students = [Student(name:"짱구", bloodType: "A"),
                Student(name:"수지", bloodType: "B"),
                Student(name:"철수", bloodType: "O"),
                Student(name:"훈이", bloodType: "A")]

 

 

Using filter operator

students.filter{$0.bloodType=="A"}.map{print("\($0.name)")}

 

Using for loops

if문을 사용할 경우에는 모든 원소에 대한 탐색을 해야 합니다. 하지만 where 조건을 사용할 때 더 간략하게 특정 Element를 filter 할 수 있습니다.

for student in students where student.bloodType == "A" {
    print("\(student.name)")
}

 

2. Using protocol extension

 

struct Student{ ... }
struct Teacher{ ... }
struct Staff{ ... }
...

만약 학생이 아니라 교수, 직원 등 여러 type의 struct 가 있을 때 A 유형인 학생의 이름을 출력할 수도 있습니다.

 

extension Array where Element == Student {
    func printBloodTypeA() {
        for student in self where student.bloodType == "A" {
            print("\(student.name)")
        }
    }
}

이 경우 위에서 선언한 students (type: Student) 변수는 printBloodTypeA() 함수를 사용할 수 있습니다. students 뿐 아니라 Element == Student를 만족하는 모든 object는 사용할 수 있습니다. 반면 Teacher, Staff 타입의  변수는 Student 타입이 아니기 때문에 Array extension에 함수를 선언했지만 사용할 수 없습니다. 단순히 커스텀으로 정의한 struct 뿐 아니라 String, Int, protocol 등 여러 타입에 대한 조건을 추가할 수 있습니다.

 

3. Using enum and switch

enum Sign: CustomStringConvertible {
    case plus
    case minus
    
    var description: String {
        switch self {
        case .plus: return "+"
        case .minus: return "-"
        }
    }
}

enum BloodType{
    typealias RH = Sign
    case A(RH)
    case B(RH)
    case O(RH)
    case AB(RH)
}

struct Student {
    let name: String
    let bloodType: BloodType
}

 

enum을 사용해서  BloodType를 정의하고 RH부호도 정의했습니다.

 

let students = [Student(name:"짱구", bloodType: .A(.plus)),
                Student(name:"수지", bloodType: .B(.minus)),
                Student(name:"철수", bloodType: .O(.plus)),
                Student(name:"훈이", bloodType: .A(.minus))]

 

 

그리고 students에서 중 A+ 타입의 혈액형을 가진 학생들만 추출할 수도 있습니다.

extension Array where Element == Student {
    func getBloodTypeAWhereRHPlus() -> [Student] {
        return self.filter { student in
            switch student.bloodType {
            case .A(let rh) where rh == .plus:
                return true
            default:
                return false
            }
        }
    }
}

students.getBloodTypeAWhereRHPlus().map{print($0.name)}
// 짱구 출력.

 

 

4. Using foundation framework's operators. lable type: where

  • first(where:)
  • firstIndex(where:)
  • removeAll(where:)
  • contains(where:)

위에서 언급한 operator 등.. 또한 클로저를 통해 조건을 정의할 수 있다는 사실!!

 

let items = ["itemA","itemB","itemC","itemD"]
for item in items where item.contains("A") || item.contains("D") {
    print(item)
}
// itemA, itemD 출력.

where 구문에서 콤마, &&, || 등을 사용해 여러 조건을 추가할 수 있습니다.

 

func test<T where: StringLiteralConvertible>(string:T) {
	print(string)
}
test("where keyword 뿌수기!!")

제너릭 또한 마찬가지로 특정 type의 조건을 where로 정할 수 있습니다.

 

 

틀린 부분 발견 시 댓글로 알려주신다면 정말 감사합니다.

 

References:

https://www.youtube.com/watch?v=-yhFrRmF_0w 

https://www.avanderlee.com/swift/where-using-swift/

https://www.appypie.com/swift-where-how-to