[英]fold/reduce with complex accumulator
I have a list that looks like this:我有一个看起来像这样的列表:
val myList = listOf(
Message(
id= 1,
info = listOf(1, 2)
),
Message(
id= 1,
info = listOf(3, 4)
),
Message(
id= 2,
info = listOf(5, 6)
)
)
How can I convert it so the elements with the same id are combined?如何转换它以便合并具有相同 id 的元素?
listOf(
Message
id= 1
info = listOf(1, 2, 3, 4)
),
Message
id= 2
info = listOf(5, 6)
)
)
I've tried the following, and it works我试过以下方法,它有效
myList
.groupBy { it.id }
.map { entry ->
val infos = entry.value.fold(listOf<Int>()) { acc, e -> acc + e.info }
Message(
id = entry.key,
info = infos
)
}
But I was wondering if there was an easier/cleaner/more idiomatic way to merge these objects.但我想知道是否有更简单/更干净/更惯用的方式来合并这些对象。 It seems like I would be able to do this with a single fold, but I can't wrap my brain around it.
似乎我可以通过一次折叠来完成此操作,但我无法全神贯注。
Thanks谢谢
You can groupingBy
the ids, then reduce
, which would perform a reduction on each of the groups.您可以按 id
groupingBy
,然后进行reduce
,这将对每个组执行归约。
myList.groupingBy { it.id }.reduce { id, acc, msg ->
Message(id, acc.info + msg.info)
}.values
This will of course create lots of Message
and List
objects, but that's the way it is, since both are immutable.这当然会创建很多
Message
和List
对象,但事实就是如此,因为它们都是不可变的。 But there is also a chance that this doesn't matter in the grand scheme of things.但也有可能这在宏伟的计划中并不重要。
If you had a MutableMessage
like this:如果您有这样的
MutableMessage
:
data class MutableMessage(
val id: Int,
val info: MutableList<Int>
)
You could do:你可以这样做:
myList.groupingBy { it.id }.reduce { _, acc, msg ->
acc.also { it.info.addAll(msg.info) }
}.values
Would also go for groupingBy
but do it a bit differently via fold
(compare also Grouping
):也将 go 用于
groupingBy
但通过fold
做的有点不同(也比较Grouping
):
myList.groupingBy { it.id }
.fold({ _, _ -> mutableListOf<Int>() }) { _, acc, el ->
acc.also { it += el.info }
}
.map { (id, infos) -> Message(id, infos) }
This way you have only 1 intermediate map and only 1 intermediate list per key, which accumulates your values.这样你只有 1 个中间 map 并且每个键只有 1 个中间列表,它会累积你的值。 At the end you transform it in the form you require (eg into a
Message
).最后,您将其转换为您需要的形式(例如转换为
Message
)。 Maybe you do not even need that?也许你甚至不需要那个? Maybe the map is already what you are after?
也许 map 已经是你想要的了?
In that case you may want to use something as follows (ie narrowing the mutable list type of the values):在这种情况下,您可能希望使用如下内容(即缩小值的可变列表类型):
val groupedMessages : Map<Int, List<Int>> = myList.groupingBy { it.id }
.fold({ _, _ -> mutableListOf() }) { _, acc, el ->
acc.also { it += el.info }
}
A solution without using reduce or fold:不使用 reduce 或 fold 的解决方案:
data class Message(val id: Int, val info: List<Int>)
val list = listOf(
Message(id = 1, info = listOf(1, 2)),
Message(id = 1, info = listOf(3, 4)),
Message(id = 2, info = listOf(5, 6))
)
val result = list
.groupBy { message -> message.id }
.map { (_, message) -> message.first().copy(info = message.map { it.info }.flatten() ) }
result.forEach(::println)
By extracting out a few functions which have a meaning of their own , You can make it readable to a great extent.通过抽取出几个有自己含义的函数,可以在很大程度上让它具有可读性。
data class Message(val id: Int, val info: List<Int>) {
fun merge(that: Message): Message = this.copy(info = this.info + that.info)
}
fun List<Message>.mergeAll() =
this.reduce { first, second -> first.merge(second) }
fun main() {
val myList = listOf(
Message(
id = 1,
info = listOf(1, 2)
),
Message(
id = 1,
info = listOf(3, 4)
),
Message(
id = 2,
info = listOf(5, 6)
)
)
val output = myList
.groupBy { it.id }
.values
.map { it.mergeAll() }
println(output)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.