我想根据对象中的两个字段对对象列表进行分组。
假设我有这样一个物体:
data class ItemDataDTO(
val itemId: BigInteger?,
val sequence: BigInteger?,
val serialNumber: String?,
val pickingId: String?,
val runId: String? = null,
val warehouse: String?,
)
现在,我有了一个包含许多项的ItemDataDTO
列表。我想按runId
和pickingId
对它们进行分组(因为我需要那些具有相同的pickingId
和runId
的项目)。
val items: List<ItemDataDTO> = someItemRepository.getItemsForWarehouse("someWarehouseId")
val groupedItems = items.groupBy({ it.runId }, { it.pickingId })
这不管用。我发现我可以使用groupingBy()
函数和一个Triple
,但我只想将它们按两个值分组.
val groupedItems = items.groupingBy { Triple(it.runId, it.pickingId, null) }
但这也不起作用。我尝试添加第三个参数,而不是null
,使用it.warehouse
val groupedItems = items.groupingBy { Triple(it.runId, it.pickingId, it.warehouse) }
它返回Grouping<ItemDataDTO, Triple<String?, String?, String?>>
的一个实例,我不知道如何处理这个对象。
我能做些什么来正确地分组这些对象呢?
在一个完美的世界里,我想把这个列表转换成这样的列表:
data class PickingList(
val runId: String,
val pickingId: String,
val items: List<ItemDataDTO>,
)
所以输出将是一个List<PickingList>
。
发布于 2022-11-25 18:26:14
它没有什么特别之处!groupBy
接受一个keySelector
函数,该函数返回一些值作为该项的键。因此,如果要匹配两个属性,则该关键项需要由这两个值组成。
包含两个项的Triple
是一个Pair
,所以您可以这样做:
// just FYI, "it.runId to it.pickingId" is shorthand for a Pair - you see it more
// when defining key/value pairs for maps though. "Pair(x, y)" might read better here
// since you're really just combining values, not describing how one relates to the other
items.groupBy { Pair(it.runId, it.pickingId) }
因此,每一项都将生成一个具有这两个值的Pair
。与Pair
匹配的任何其他项(就equals
函数而言)都将放在同一个组中。这有点像添加到一个Map
,只是如果一个键已经存在,这个值就会添加到一个列表中,而不是覆盖之前的值。
你真的可以用任何钥匙做到这一点。Pair
和Triple
只是打包几个项目的快速、通用的方便类--但是很多时候最好定义自己的数据结构,例如使用data class
。只要具有相同数据的两个实例相等,它们就被计算为分组的相同键。
至于你想要的输出,用PickingList
.您可以在分组操作中使用类似的东西--但在这种情况下,您必须自己重新实现groupBy
。您必须获取一个项,并从要考虑的属性中计算出它的复合键。然后,您需要在为组创建的某个商店中找到该密钥的匹配项。
如果是PickingList
的列表,则需要遍历每个If,将其if与所需的if进行比较,如果找到匹配,则将其添加到列表中,如果找不到则创建对象。
如果您存储的是Pair(id1, id2) -> PickingList
映射,那么在生成查找键方面,这与groupBy
的工作方式非常接近。在这种情况下,您可能只想使用groupBy
对所有项进行分组,然后转换最终的映射:
items.groupBy { Pair(it.runId, it.pickingId) }
.map { (ids, list) ->
PairingList(runId = ids.first, pickingId = ids.second, items = list)
}
这将获取每个映射条目(ID的Pair
和按这些ID分组的所有事物的列表),并使用它从键/值数据创建PairingList
。基本上,一旦对所有数据进行了分组,就可以将其转换为要使用的数据结构。
这也是一个很好的例子,说明为什么您自己的数据类可能比仅仅使用Pair
更好-- it.first
并没有真正告诉您这个值在Pair
中是什么,只是它是两个值中的第一个。鉴于
data class IdCombo(val runId: String, val pickingId: String)
它的工作原理与Pair
相同,但这些属性具有有用的名称,使您的代码更加可读性更强,更不容易出现bug:
map { (ids, list) ->
// didn't even bother with the named arguments, since the names are in
// the ids object now!
PairingList(ids.runId, ids.pickingId, items = list)
}
https://stackoverflow.com/questions/74575684
复制相似问题