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

前端 未结 2 1769
天命终不由人
天命终不由人 2021-01-03 02:15

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 i

相关标签:
2条回答
  • 2021-01-03 02:59

    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)
    
    0 讨论(0)
  • 2021-01-03 03:10

    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]
    
    0 讨论(0)
提交回复
热议问题