使用函数式编程计算每个标记的出现次数

Sch*_*ude 5 functional-programming kotlin

我一直在尝试创建一个函数,该函数返回一个Map<String, Int>键,其中键是某个标记,值是出现次数.

我需要从中提取信息的对象(简化):

class Note {
   List<String> tags
}
Run Code Online (Sandbox Code Playgroud)

到目前为止的功能:

private fun extractTags(notes: List<Note>): Map<String, Int> {
    return notes.map { note -> note.tags }
                .groupBy { it }
                .mapValues { it.value.count() }    
}
Run Code Online (Sandbox Code Playgroud)

现在编译器给我一个返回类型不匹配,Map<(Mutable)Set<String!>!, Int>我不确定我得到了所需的结果(因为我仍然无法正确测试).

我期待的结果有以下几点:

(tag1, 1)
(tag2, 4)
(tag3, 14)
...
Run Code Online (Sandbox Code Playgroud)

hol*_*ava 8

您可以像在Kotlin中使用Java-8 stream-api一样使用Iterable#asSequence.然后使用Sequence#flatMap将所有tags 合并为a Sequence,然后使用Sequence#groupingBy计算每个标记,例如:

private fun extractTags(notes: List<Note>): Map<String, Int> {
    return notes.asSequence()
                .flatMap { it.tags.asSequence() }
                .groupingBy { it }.eachCount()
}
Run Code Online (Sandbox Code Playgroud)

注意:Sequence#flatMapSequence#groupingBy都是中间操作,这意味着如果没有调用终端操作 Grouping#eachCount.所有的操作Sequence都没有运行.


zsm*_*b13 5

虽然已经接受的答案无可争议地解决了你的问题,但我觉得这里有一些“当你拿着锤子时,一切看起来都像钉子”的事情。

该答案的本质是flatMap, groupingBy, 和eachCount是您解决问题所需的方法,但是,在这里使用序列似乎完全没有必要。

这是仅对常规集合进行操作/使用常规集合的代码:

private fun extractTags(notes: List<Note>): Map<String, Int> {
    return notes.flatMap { it.tags }
            .groupingBy { it }
            .eachCount()
}
Run Code Online (Sandbox Code Playgroud)

我想争辩说,这是比使用序列更好的解决方案,因为:

  • 它产生相同的结果,因为它使用相同的运算符。
  • 没有它们,代码更简单,更容易阅读。
  • 这里的转换很简单而且很少,当你有长链时,序列会很有用。
  • 我们可能在这里对相对较小的数据集进行操作。在我自己的快速测量中,当有一百万个音符时,使用序列的解决方案大约快 10%,但当只有一万个音符时慢 17%。我敢打赌,您的列表规模更接近后者。序列有开销。
  • 我们根本没有利用序列提供的惰性,因为我们想立即评估并返回结果。

您还可以在此处查看两种方式的优缺点的出色比较,以了解更多详细信息。