While Swift's Sequence
models brings a lot of niceties that we didn't have access to in Objective-C, like map
and filter
, there are other useful operations on Sequences that the standard library doesn't support yet. I'd like to pitch one today: count(where:)
, which counts the number of objects in a Sequence
that passes some test.
Usage
While this behavior can currently be approximated with a filter
and a count
, that approach creates an intermediate array which it immediately discards. This is a bit wasteful, especially for long sequences:
// not as good
[1, 2, 3, -1, -2].filter({ $0 > 0 }).count // => 3
Simple usage:
// good
[1, 2, 3, -1, -2].count(where: { $0 > 0 }) // => 3
I use count(where:)
in my code regularly, and I think it'd make a nice addition to the standard library.
Naming
In earlier versions of Swift, first
and first(where:)
would collide, causing bad errors. This is a potential problem here as well (because count
exists on Collection
). However, I think count(where:)
is the best name for this function, and if it causes bad diagnostics, we should treat those as bugs to be fixed. However, I'm interested in hearing any other names for this function.
Equatable version
One thing that we should discuss is if there should be a version (perhaps called count(of:)
?) that works on Sequences whose Element
is Equatable
and returns the count of all objects that are equal to the parameter. I've never had a use for this function, but I would be open to including it in the proposal if other people want it.
Proposal and Implementation
If this pitch is regarded well, I'd be happy to write a more detailed proposal and open a pull request with an implementation and tests!
A sample implementation
extension Sequence {
func count(where predicate: (Element) throws -> Bool) rethrows -> Int {
var count = 0
for element in self {
if try predicate(element) {
count += 1
}
}
return count
}
}
extension Sequence where Element: Equatable {
func count(of elementToTest: Element) -> Int {
var count = 0
for element in self {
if element == elementToTest {
count += 1
}
}
return count
}
}