我一直试图创建一个返回Map<String, Int>
的函数,其中键是某个标记,值是出现次数。
我需要从中提取信息的对象(简化):
class Note {
List<String> tags
}
到目前为止的功能:
private fun extractTags(notes: List<Note>): Map<String, Int> {
return notes.map { note -> note.tags }
.groupBy { it }
.mapValues { it.value.count() }
}
现在编译器给我一个Map<(Mutable)Set<String!>!, Int>
的返回类型不匹配,我不确定我是否得到了预期的结果(因为我仍然无法正确测试)。
我希望结果符合以下几行:
(tag1, 1)
(tag2, 4)
(tag3, 14)
...
答案 0 :(得分:8)
您可以像在Kotlin中使用Java-8 stream-api一样使用Iterable#asSequence。然后使用Sequence#flatMap将所有tag
合并为Sequence
,然后使用Sequence#groupingBy计算每个标记,例如:
private fun extractTags(notes: List<Note>): Map<String, Int> {
return notes.asSequence()
.flatMap { it.tags.asSequence() }
.groupingBy { it }.eachCount()
}
注意:Sequence#flatMap和Sequence#groupingBy都是intermediate operations,这意味着如果未调用terminal operation Grouping#eachCount
。 Sequence
上的所有操作都没有运行。
答案 1 :(得分:3)
虽然已经接受的答案无可争议地解决了你的问题,但我觉得这里有一点“当你有锤子时,一切看起来像钉子”。
答案的实质是flatMap
,groupingBy
和eachCount
是解决问题所需的方法,但是,在这里使用序列似乎完全没必要。
以下是对常规集合进行操作的代码:
private fun extractTags(notes: List<Note>): Map<String, Int> {
return notes.flatMap { it.tags }
.groupingBy { it }
.eachCount()
}
我想说这是一个比使用序列更好的解决方案,因为:
您可以看到两种方式与利弊here的完美比较以及更多细节。
答案 2 :(得分:0)
以下是您修改后的代码。我将map
更改为flatMap
。我还提供了一个实现为扩展功能的版本。你的失败是因为map>
正在产生List<List<String>>
,你期待List<String>
(因此,flagMap
)。
fun extractTags(notes: List<Note>): Map<String, Int> {
return notes.flatMap { it.tags } // results in List<String>
.groupBy { it } // results in Pair<String, List<String>>
.mapValues { it.value.count() }
}
fun Iterable<Note>.extractTags(): Map<String, Int> {
return this.flatMap { it.tags } // results in List<String>
.groupBy { it } // results in Pair<String, List<String>>
.mapValues { it.value.count() }
}
以下是使用
测试它的一些代码import kotlin.collections.*
fun main(vararg args: String) : Unit {
var notes = ArrayList<Note>()
notes.add(Note(List<String>(1) { "tag1" }))
notes.add(Note(List<String>(4) { "tag4" }))
notes.add(Note(List<String>(14) { "tag14" }))
for((first,second) in extractTags(notes))
println("$first: $second")
for((first,second) in notes.extractTags())
println("$first: $second")
}
class Note {
constructor(strings: List<String>) {
tags = strings
}
var tags: List<String>
}
答案 3 :(得分:0)
不好意思,但是这是最好的解决方案:我认为,当您使用Kotlin时,您拥有的标准库比Java 8流提供了更好的语法,更短,更干净。
private fun extractTags(notes: List<Note>): Map<String, Int> = notes.flatMap { it.tags }//list of String
.groupBy { it }//list of Map.Entry<String,List<String>> //List<Map.Entry<String,List<String>>>
.map {
Pair(it.key, it.value.size)
}//list of pairs(tag, count) // List<Pair(String,Int)
.toMap()//creat a map from the list of pairs