You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
There's 2 relatively short ways to achieve this today:
Using reduce: input.reduce(into: [:]) { $0[default: 0] += 1 }
Reduce is really general, and isn't particularly readable, especially for beginners. The performance here is good though, allocating a single dictionary and mutating it in-place.
Using group(by:): group(by: { $0 }).mapValues(\.count)
We could use the group(by:) helper that I added to Swift Algorithms, but it allocates a ton of intermediate arrays for all the groups, when all we need is their counts.
Proposed solution
The exact name is TBD, but I'm proposing a function like:
We could also consider taking a by: parameter, to count things by a value other than themselves. Though perhaps .lazy.map would be better. E.g. input.tallied(by: \.foo) could be expressed as input.lazy.map(\.foo).tallied()
Alternatives
A more general "collectors" API
Similar to Java collectors, which let you express transformations over streams, collecting into Arrays, Dictionaries, Counters, or anything else you might like.
This could pair well with Swift Collections, e.g. if we added a new CountedSet (a native Swift alternative to NSCountedSet. E.g. we could have:
@xwu Oh cool, I didn't notice there was already an issue for a counted set.
Even if it did exist, I think we should expose a method on Sequence for producing it. For the same reason why input.grouped(by: ...) is nicer than Dictionary(grouping: input, by: ...) (see rationale and PR). In that case, that would make more sense to live in Swift Collections.
Goal
It pretty common to want to take a collection of items, and count the number of occurrences of each item. e.g.
Today
There's 2 relatively short ways to achieve this today:
Using
reduce
:input.reduce(into: [:]) { $0[default: 0] += 1 }
Reduce is really general, and isn't particularly readable, especially for beginners. The performance here is good though, allocating a single dictionary and mutating it in-place.
Using
group(by:)
:group(by: { $0 }).mapValues(\.count)
We could use the
group(by:)
helper that I added to Swift Algorithms, but it allocates a ton of intermediate arrays for all the groups, when all we need is their counts.Proposed solution
The exact name is TBD, but I'm proposing a function like:
We could also consider taking a
by:
parameter, to count things by a value other than themselves. Though perhaps.lazy.map
would be better. E.g.input.tallied(by: \.foo)
could be expressed asinput.lazy.map(\.foo).tallied()
Alternatives
A more general "collectors" API
Similar to Java collectors, which let you express transformations over streams, collecting into Arrays, Dictionaries, Counters, or anything else you might like.
This could pair well with Swift Collections, e.g. if we added a new
CountedSet
(a native Swift alternative toNSCountedSet
. E.g. we could have:Prior art
collections.Counter
tally
java.util.stream.Collectors.counting()
countBy
C#, Rust don't have helpers for this.
The text was updated successfully, but these errors were encountered: