Filter 함수 구현해 보기

2018-09-28

개요

지난 시간에는 Map, Reduce, Filter 함수 중 Map 함수를 구현해 봤습니다. 이번 시간에는 Filter 함수를 구현해 보도록 하겠습니다.

이 포스트를 제대로 이해하기 위해서는 Closure와 Generic에 대한 이해가 선행돼야 합니다. 만약 익숙하지 않으시다면 Swift 공식문서의 Closure(영어, 한국어)와 Generic(영어, 한국어) 섹션의 정독을 권해드립니다.

Filter 함수의 동작

filter함수는 이름에서 유추해 볼 수 있다시피 주어진 콜렉션에서 특정 조건의 원소만 걸러서 반환해주는 함수입니다. 예를 들어, 입력이 다음과 같고

let sampleFiles = ["HowTo.md","HelloiPhone.swift","HelloSwift.swift","HelloProgrammers.swift","README.md"]

이 콜렉션 중 확장자가 md인 파일만 뽑아내고 싶은 경우 다음과 같이 filter함수를 사용할 수 있습니다.

let filteredFiles = sampleFiles.filter { $0.hasSuffix(".md") }
print("Filtered Files : \(filteredFiles)")

위 코드를 실행한 결과는 다음과 같습니다.

Filtered Files : ["HowTo.md", "README.md"]

그럼 지금부터 위 예제로부터 filter함수를 어떻게 구현할 수 있는지 알아보도록 하겠습니다.

Filter 함수의 구현

앞의 예제에서는 콜렉션에서 확장자가 md인 파일만 뽑아냈습니다. 같은 기능을 수행하는 함수를 다음과 같이 만들 수 있습니다.

func getMDFiles(files: [String]) -> [String] {
        var result = [String]()
        
        for file in files {
            if file.hasSuffix(".md") {
                result.append(file)
            }
        }
        
        return result
    }

이 함수에서 핵심은 if file.hasSuffix(".md") 이 부분이라고 할 수 있습니다. 이 조건을 만족하는 원소만 수집해서 반환하게 되는 것이죠. 그래서 이 부분을 함수화 시키면 입력하는 함수의 조건대로 필터링을 수행하는 함수를 만들 수 있을 것 입니다.

Generic을 이용해 customFilter함수를 만들면 다음과 같습니다.

func customFilter<T>(xs: [T], check: (T) -> Bool) -> [T] {
        var result = [T]()
        
        for x in xs {
            if check(x) {
                result.append(x)
            }
        }
        
        return result
    }

콜렉션을 입력으로 넣고 추출할 원소의 조건을 함수로 check()에 넣어줍니다. 그러면 입력한 함수에 해당하는 조건을 만족하는 원소만을 추출해 반환해 줍니다.

let filteredFiles = sampleFiles.filter { $0.hasSuffix(".md") }
print("Filtered Files : \(filteredFiles)")

위 함수를 실행하면 앞서와 동일한 결과를 얻을 수 있습니다.

CustomFilter Result : ["HowTo.md", "README.md"]

지난 번과 마찬가지로 저희가 구현한 customFilter 함수를 Array에 추가해 Swift에서 기본으로 제공하는 filter함수와 같이 동작하도록 만들기 위해 다음과 같이 Array 클래스에 customFilter를 확장해 추가해 보도록 하겠습니다.

extension Array {
    func customFilter(check: (Element) -> Bool) -> [Element] {
        var result = [Element]()
        
        for x in self {
            if check(x) {
                result.append(x)
            }
        }
        
        return result
    }
}

이제 Array에서 저희가 구현한 customFilter를 사용할 수 있습니다. 😎

Filter 함수와 CustomFilter 함수의 동작

이제 Swift에서 기본적으로 제공해주는 filter의 동작과 저희가 구현한 customFilter의 동작을 비교해 보도록 하겠습니다.

[소스코드]

let sampleFiles = ["HowTo.md","HelloiPhone.swift","HelloSwift.swift",
                           "HelloProgrammers.swift","README.md"]
        
    let filterResult1 = sampleFiles.filter{ $0.hasSuffix(".md") }
    let customFilterResult1 = sampleFiles.customFilter{ $0.hasSuffix(".md") }
    
    let filterResult2 = sampleFiles.filter{ $0.hasPrefix("Hello") }
    let customFilterResult2 = sampleFiles.customFilter{ $0.hasPrefix("Hello") }
    
    let filterResult3 = sampleFiles.filter{ $0.contains("README") }
    let customFilterResult3 = sampleFiles.customFilter{ $0.contains("README") }
    
    print("Filter             #1 : \(filterResult1)")
    print("customFilterResult #1 : \(customFilterResult1)")
    print("======================")
    print("Filter             #2 : \(filterResult2)")
    print("customFilterResult #2 : \(customFilterResult2)")
    print("======================")
    print("Filter             #3 : \(filterResult3)")
    print("customFilterResult #3 : \(customFilterResult3)")

[실행결과]

Filter             #1 : ["HowTo.md", "README.md"]
customFilterResult #1 : ["HowTo.md", "README.md"]
======================
Filter             #2 : ["HelloiPhone.swift", "HelloSwift.swift", "HelloProgrammers.swift"]
customFilterResult #2 : ["HelloiPhone.swift", "HelloSwift.swift", "HelloProgrammers.swift"]
======================
Filter             #3 : ["README.md"]
customFilterResult #3 : ["README.md"]

Swift에서 제공하는 filter함수와 저희가 만든 customFilter함수가 동일하게 동작하는 것을 확인할 수 있습니다.

결론

filter함수 역시 쉽게 구현할 수 있다는 것을 확인 할 수 있었습니다. 다음 포스트에서는 map, reduce, filter함수중 남은 reduce 함수를 직접 구현해 보겠습니다. 😄