Limit the results of a Swift array filter to X for performance

柔情痞子 提交于 2019-11-30 15:57:02

问题


I have about 2000 elements in my array, and when it is filtered, I would like to end the filtering as soon as I have 5 elements in my filtered array.

Currently it is:

providerArray.filter({($0.lowercased().range(of:((row.value as? String)?.lowercased())!) != nil)})

which can return up to 2000 results which is a waste of processing, and time.

To be more clear, I need a solution that is equivalent to limiting the filter results like I can with coreData fetches [request setFetchLimit:5];


回答1:


The fastest solution in terms of execution time seems to be an explicit loop which adds matching elements until the limit is reached:

extension Sequence {
    public func filter(where isIncluded: (Iterator.Element) -> Bool, limit: Int) -> [Iterator.Element] {
        var result : [Iterator.Element] = []
        result.reserveCapacity(limit)
        var count = 0
        var it = makeIterator()

        // While limit not reached and there are more elements ...
        while count < limit, let element = it.next() {
            if isIncluded(element) {
                result.append(element)
                count += 1
            }
        }
        return result
    }
}

Example usage:

let numbers = Array(0 ..< 2000)
let result = numbers.filter(where: { $0 % 3 == 0 }, limit: 5)
print(result) // [0, 3, 6, 9, 12]



回答2:


You can use .lazy too boost the performance a bit:

let numbers: [Int] = Array(0 ..< 2000)

let result: AnySequence = numbers
    .lazy
    .filter {
        print("Calling filter for: \($0)")
        return ($0 % 3) == 0
    }
    .prefix(5)

print(Array(result))

This will call the filter function only for the first 15 values (until it finds 5 that pass the filter).

Now you can concentrate on boosting the performance of the filter itself. E.g. by caching values. You don't have to do this but if some values keep repeating, it can boost the performance a lot.

let numbers: [Int] = Array(0 ..< 2000)
var filterCache: [Int: Bool] = [:]

let result: AnySequence = numbers
    .lazy
    .filter {
        if let cachedResult = filterCache[$0] {
            return cachedResult
        }

        print("Calling filter for: \($0)")
        let result = (($0 % 3) == 0)

        filterCache[$0] = result

        return result
    }
    .prefix(5)

print(Array(result))

You can apply this method directly to your function.

Also note that to boost performance, you should either:

  • save ((row.value as? String)?.lowercased())! into a local variable because it is executed multiple times

  • simplify the expression using options:

 let result: AnySequence = providerArray
     .lazy
     .filter {
       $0.range(of: row.value as! String, options: [.caseInsensitive]) != nil
     }
     .prefix(5)


来源:https://stackoverflow.com/questions/41871861/limit-the-results-of-a-swift-array-filter-to-x-for-performance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!