在 Kotlin 中根据给定的键列表对映射进行切片

问题描述 投票:0回答:5

给定地图和钥匙列表

val abc = mapOf(1 to "a", 2 to "b", 3 to "c")
val keys = listOf(1, 2)

如何获取仅包含

keys
指定的键值对的映射?类似的东西

val ab = abc.slice(keys)
// equivalent to mapOf(1 to "a", 2 to "b)

我正在寻找比

更优雅的东西
val ab = listOf(1, 2).map { it to abc[it] }.toMap()

例如,在 Elixir 中:

abc = %{1 => "a", 2 => "b", 3 => "c"}
ab = Map.take(abc, [1, 2])
# equivalent to ab = %{1 => "a", 2 => "b"}
dictionary kotlin slice
5个回答
5
投票

您可以使用

filterKeys

val ab = abc.filterKeys { it in keys }

而且由于它是 Kotlin,您甚至可以定义自己的扩展函数来实现您的想象:

fun <T> Map<T, *>.slice(keys: Iterable<T>) = filterKeys { it in keys }

val ab = abc.slice(keys)

3
投票

上述答案中给出的解决方案确实解决了问题,但我认为有必要进行一些小的更改。

问题是,对于

key
中的每个
map
,它们检查
list
contains
是否为
key
,这是
O(n)
操作,这对于小列表来说是可以的,但是一旦达到一定大小,变得非常慢。我建议您将键的
list
转换为
set
,这样在平均情况下将包含操作减少为
O(1)
。 (因此减少碳足迹:))。

以下是合并上述更改的解决方案。

val mapAbc = mapOf(1 to "a", 2 to "b", 3 to "c")
val keySet = listOf(1, 2).toSet()
val filteredMap = mapAbc.filterKeys { it in keySet }

1
投票
abc.filterKeys { it in listOf(1, 2) }

1
投票
fun <K, V> Map<K, V>.slice(keys: Collection<K>): Map<K,V> {
    val resultMap = mutableMapOf<K, V>()
    for (key in keys) {
        get(key)?.let{resultMap[key] = it}
    }
    return resultMap
}

测试:

@Test
fun `should slice map by keys`() {
    // GIVEN

    val inputMap = (1..10_000).associate {
        UUID.randomUUID() to "value $it"
    }

    val nonExistingKey = UUID.randomUUID()
    val filterKeys = inputMap.keys.take(999).shuffled() + nonExistingKey

    // WHEN

    val startSlice = System.currentTimeMillis()
    val filteredMapSlice = inputMap.slice(filterKeys)
    println("Duration of slice: ${System.currentTimeMillis() - startSlice} ms")

    val startFilterKeys = System.currentTimeMillis()
    val filteredMapFilterKeys = inputMap.filterKeys { it in filterKeys }
    println("Duration of filterKeys: ${System.currentTimeMillis() - startFilterKeys} ms")
    assertThat(filteredMapFilterKeys).isEqualTo(filteredMapSlice)

    // THEN
    
assertThat(filteredMapSlice).hasSize(filterKeys.size - 1) // non-existing key should have been filtered
    assertThat(filteredMapSlice.keys).doesNotContain(nonExistingKey)
    assertThat(filteredMapSlice.keys).allMatch { it in inputMap.keys }
    filteredMapSlice.forEach{ (key, value) ->
        assertThat(value).isEqualTo(inputMap[key])
    }
}

这比

.filterKeys()

快了几个数量级(!)
Duration of slice: 3 ms
Duration of filterKeys: 1479 ms

0
投票

@spyro 的有效答案的微小变化:

fun <K, V> Map<K, V>.slice(keys: Collection<K>): Map<K,V> =
    buildMap {
        keys.forEach { key ->
            this@slice[key]?.let { value ->
                put(key, value)
            }
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.