Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficiency of using filter{where:} vs. removeAll{where:} when modifying a parameter value

Tags:

swift

swift4.2

Swift 4.2 introduced a new removeAll {where:} function. From what I have read, it is supposed to be more efficient than using filter {where:}. I have several scenarios in my code like this:

private func getListOfNullDates(list: [MyObject]) -> [MyObject] {
        return list.filter{ $0.date == nil }
            .sorted { $0.account?.name < $1.account?.name }
    }

However, I cannot use removeAll{where:} with a param because it is a constant. So I would need to redefine it like this:

private func getListOfNullDates(list: [MyObject]) -> [MyObject] {
        var list = list
        list.removeAll { $0.date == nil }
        return list.sorted { $0.account?.name < $1.account?.name }
    }

Is using the removeAll function still more efficient in this scenario? Or is it better to stick with using the filter function?

like image 406
Kate M Avatar asked Apr 09 '19 18:04

Kate M


2 Answers

Thank you for this question 🙏🏻

I've benchmarked both functions using this code on TIO:

let array = Array(0..<10_000_000)

do {
    let start = Date()
    let filtering = array.filter { $0 % 2 == 0 }
    let end = Date()

    print(filtering.count, filtering.last!, end.timeIntervalSince(start))
}

do {
    let start = Date()
    var removing = array
    removing.removeAll { $0 % 2 == 0 }
    let end = Date()

    print(removing.count, removing.last!, end.timeIntervalSince(start))
}

(To have the removing and filtering identical, the closure passed to removeAll should have been { $0 % 2 != 0 }, but I didn't want to give an advantage to either snippet by using a faster or slower comparison operator.)

And indeed, removeAll(where:) is faster when the probability of removing elements (let's call it Pr)is 50%! Here are the benchmark results :

filter    : 94ms
removeAll : 74ms

This is the same case when Pr is less than 50%.

Otherwise, filtering is faster for a higher Pr.

One thing to bear in mind is that in your code list is mutable, and that opens the possibility for accidental modifications.

Personally, I would choose performance over old habits, and in a sense, this use case is more readable since the intention is clearer.


Bonus : Removing in-place

What's meant by removing in-place is the swap the elements in the array in such a way that the elements to be removed are placed after a certain pivot index. The elements to keep are the ones before the pivot element :

var inPlace = array
let p = inPlace.partition { $0 % 2 == 0 } 

Bear in mind that partition(by:) doesn't keep the original order.

This approach clocks better than removeAll(where:)

like image 125
ielyamani Avatar answered Nov 19 '22 10:11

ielyamani


Beware of premature optimization. The efficiency of a method often depends on your specific data and configuration, and unless you're working with a large data set or performing many operations at once, it's not likely to have a significant impact either way. Unless it does, you should favor the more readable and maintainable solution.

As a general rule, just use removeAll when you want to mutate the original array and filter when you don't. If you've identified it as a potential bottleneck in your program, then test it to see if there's a performance difference.

like image 4
John Montgomery Avatar answered Nov 19 '22 11:11

John Montgomery