Count occurrences of each tag using functional programming
You can using Iterable#asSequence just like as Java-8 stream-api in Kotlin. then using Sequence#flatMap to merge all tag
s into a Sequence
, and then using Sequence#groupingBy to counting each tag, for example:
private fun extractTags(notes: List<Note>): Map<String, Int> {
return notes.asSequence()
.flatMap { it.tags.asSequence() }
.groupingBy { it }.eachCount()
}
Note: both Sequence#flatMap and Sequence#groupingBy are intermediate operations, which means if the terminal operation Grouping#eachCount
is not called. all of the operations on the Sequence
is not ran.
While the already accepted answer unarguably solves your problem, I feel like there's a bit of an "everything looks like a nail when you have a hammer" thing going on here.
The essence of that answer is that flatMap
, groupingBy
, and eachCount
are the methods you need to solve your problem, however, using sequences here seems completely unnecessary.
Here's the code that just operates on/with regular collections:
private fun extractTags(notes: List<Note>): Map<String, Int> {
return notes.flatMap { it.tags }
.groupingBy { it }
.eachCount()
}
I'd like to argue that this is a better solution than the one using sequences, because:
- It produces the same results, since it uses the same operators.
- The code is just simpler and easier to read without them.
- The transformations here are simple and few, sequences get useful when you have long chains.
- We are probably operating on relatively small data sets here. In my own quick measurements, the solution using sequences was about 10% faster when there are a million notes, but 17% slower when there are only ten thousand. I'll wager to guess you're closer to the latter in size of your lists. Sequences have overhead.
- We aren't making use of the laziness provided by sequences at all, since we want to evaluate and return the results immediately.
You can see an excellent comparison of the two ways with pros and cons here as well for more details.