Limit Swift array filter results to X for performance

I have about 2000 elements in my array, and when it is filtered, I would like to complete 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 are a waste of processing and time.

To be more clear, I need a solution equivalent to limiting the filter results, as I can, using the sets coreData [request setFetchLimit:5];

+4
ios swift nspredicate
source share
2 answers

The fastest solution in terms of runtime is an explicit loop that adds the appropriate 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 } } 

Usage example:

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

You can use .lazy to slightly improve performance:

 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 focus on improving the performance of the filter itself. For example. by caching values. You do not have to do this, but if some values ​​are repeated, this can significantly improve performance.

 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 improve performance you should:

  • save ((row.value as? String)?.lowercased())! to a local variable since it is executed several times

  • simplify the expression with parameters:

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

All Articles