问题
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 timessimplify 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