【发布时间】:2023-04-09 21:21:01
【问题描述】:
我有两个列表:一个包含应保留 Boolean 的旧数据,以及应与旧数据合并的新数据。通过这个单元测试可以最好地看到这一点:
@Test
fun mergeNewDataWithOld() {
// dog names can be treated as unique IDs here
data class Dog(val id: String, val owner: String)
val dogsAreCute: List<Pair<Dog, Boolean>> = listOf(
Dog("Kessi", "Marc") to true,
Dog("Rocky", "Martin") to false,
Dog("Molly", "Martin") to true
)
// loaded by the backend, so can contain new data
val newDogs: List<Dog> = listOf(
Dog("Kessi", "Marc"),
Dog("Rocky", "Marc"),
Dog("Buddy", "Martin")
)
// this should be the result: an intersection that preserves the extra Boolean,
// but replaces dogs by their new updated data
val expected = listOf(
newDogs[0] to true,
newDogs[1] to false
)
// HERE: this is the code I use to get the expected union that should contain
// the `Boolean` value of the old list, but all new `Dog` instances by the new list:
val oldDogsMap = dogsAreCute.associate { it.first.id to it }
val newDogsMap = newDogs.associateBy { it.id }
val actual = oldDogsMap
.filterKeys { newDogsMap.containsKey(it) }
.map { newDogsMap[it.key]!! to it.value.second }
assertEquals(expected, actual)
}
我的问题是:编写代码以获取我的actual 变量的更好方法是什么?我特别不喜欢我首先过滤 both 列表中包含的键,然后我必须显式使用 newDogsMap[it.key]!! 来获取 null 安全值。
我该如何改进它?
编辑:重新定义问题
感谢 Marko 更新:我想做一个交集,而不是一个并集。 很容易在列表上做一个交集:
val list1 = listOf(1, 2, 3)
val list2 = listOf(4, 3, 2)
list1.intersect(list2)
// [2, 3]
但我真正想要的是地图上的十字路口:
val map1 = mapOf(1 to true, 2 to false, 3 to true)
val map2 = mapOf(4 to "four", 3 to "three", 2 to "two")
// TODO: how to do get the intersection of maps?
// For example something like:
// [2 to Pair(false, "two"), 3 to Pair(true, "three")]
【问题讨论】:
-
你说“联合”,但你的代码做了一个交集。
expected仅包含更新的条目(它们都已经存在并且是更新批次的一部分)。 -
你说得对——我想做一个交叉路口。但我需要的是地图上的交叉点——据我所知 Kotlin 只支持列表上的交叉点。
-
如果你的两个映射包含相同的值类型,你可以使用
merge,只要你有一个MutableMap。如果您没有并且不想与null-safe-operators 合作,那么我不知道任何比显示的更简单的方法。相反,它只会变得更复杂,或者您编写更多代码,我认为省略另一个null-safe-operator 不会使其更具可读性;-)
标签: unit-testing collections kotlin functional-programming