groupByTo 在 Kotlin 中返回 emptySet。

huangapple go评论74阅读模式
英文:

groupByTo return emptySet in Kotlin

问题

val input = "perm1|0,perm2|2,perm2|1"

val output = input.split(",")
    .map { it.split("|") }
    .groupByTo(
        mutableMapOf(),
        keySelector = { it[0] },
        valueTransform = { if (it[1] != "0") setOf(it[1].toLong()) else emptySet() }
    )
    .mapValues { it.value.reduce { acc, set -> acc + set } }

val desiredOutput = output.map { (key, values) ->
    "$key [${values.joinToString(", ")}]"
}.joinToString(", ", "{", " }")

println(desiredOutput)
英文:

I have string like this.

val input = "perm1|0,perm2|2,perm2|1"

Desired output type is

val output: Set<String, Set<Long>>

and desired output value is

{perm1 [], perm2 [1,2] }

Here I need empty set if value is 0. I am using groupByTo like this

val output = input.split(",")
                  .map { it.split("|") }
                  .groupByTo(
                       mutableMapOf(),
                       keySelector = { it[0] },
                       valueTransform = { it[1].toLong()  }
                   )

However the output structure is like this

MutableMap<String, MutableList<Long>> 

and output is

{perm1 [0], perm2 [1,2] }

I am looking for best way to get desired output without using imperative style like this.

val output = mutableMapOf<String, Set<Long>>()
input.split(",").forEach {

    val t = it.split("|")

    if (t[1].contentEquals("0")) {

        output[t[0]] = mutableSetOf()
    }

    if (output.containsKey(t[0]) && !t[1].contentEquals("0")) {

        output[t[0]] = output[t[0]]!! + t[1].toLong()
    }

    if (!output.containsKey(t[0]) && !t[1].contentEquals("0")) {

        output[t[0]] = mutableSetOf()
        output[t[0]] = output[t[0]]!! + t[1].toLong()
    }
}

答案1

得分: 5

你可以简单地使用mapValues将值的类型从List<Long>转换为Set<Long>

var res: Map<String, Set<Long>> = input.split(",")
    .map { it.split("|") }
    .groupBy({ it[0] }, { it[1].toLong() })
    .mapValues { it.value.toSet() }

如果你想要用空集合来替换0的列表,可以使用if表达式来实现

var res: Map<String, Set<Long>> = input.split(",")
    .map { it.split("|") }
    .groupBy({ it[0] }, { it[1].toLong() })
    .mapValues { if (it.value == listOf<Long>(0)) setOf() else it.value.toSet() }
英文:

You can simply use mapValues to convert values type from List<Long> to Set<Long>

var res : Map<String, Set<Long>> = input.split(",")
    .map { it.split("|") }
    .groupBy( {it[0]}, {it[1].toLong()} )
    .mapValues { it.value.toSet() }

And of you want to replace list of 0 with empty set you can do it using if-expression

var res : Map<String, Set<Long>> = input.split(",")
    .map { it.split("|") }
    .groupBy( {it[0]}, {it[1].toLong()} )
    .mapValues { if(it.value == listOf<Long>(0))  setOf() else it.value.toSet() }

答案2

得分: 1

注意,您不能使用带有键值对的 Set,结果将是 map 类型。下面的代码会将值按照排序后的集合方式返回。

val result = "perm1|0,perm2|2,perm2|1".split(",")
        .map {
            val split = it.split("|")
            split[0] to split[1].toLong()
        }.groupBy({ it.first }, { it.second })
        .mapValues { it.value.toSortedSet() }
英文:

Note that you cannot have Set with key-value pair, result will be of type map. Below code gives sorted set in the values.

val result = "perm1|0,perm2|2,perm2|1".split(",")
        .map {
            val split = it.split("|")
            split[0] to split[1].toLong()
        }.groupBy({ it.first }, { it.second })
        .mapValues { it.value.toSortedSet() }

答案3

得分: 0

以下是您要的翻译内容:

虽然其他答案可能更容易理解,它们在其中构建了立即被丢弃的列表和映射,这些列表和映射基本上在下一次操作之后就被丢弃了。以下尝试使用 splitToSequence (Sequences) 和 groupingBy(请参阅 分组底部部分)来避免这种情况:

val result: Map<String, Set<Long>> = input.splitToSequence(',')
    .map { it.split('|', limit = 2) }
    .groupingBy { it[0] }
    .fold({ _, _ -> mutableSetOf<Long>() }) { _, accumulator, element ->
      accumulator.also {
        it.add(element[1].toLong()))
      }
    }

当然,您也可以在 fold 步骤中通过一个简单的条件来过滤掉集合中添加 0 的部分:

// 用于跳过 0 值但保留键的替代 fold
.fold({ _, _ -> mutableSetOf<Long>() }) { _, accumulator, element ->
  accumulator.also {
    val value = element[1].toLong()
    if (value != 0L)
      it.add(value)
  }
}

或者,也可以进行聚合,但是您的 result 变量需要更改为 Map<String, MutableSet<Long>>

val result: Map<String, MutableSet<Long>> = // ...
.aggregate { _, accumulator, element, first ->
  (if (first) mutableSetOf<Long>() else accumulator!!).also {
    val value = element[1].toLong()
    if (value != 0L)
      it.add(value)
  }
}
英文:

While the other answer(s) might be easier to grasp, they build immediate lists and maps in between, that are basically discarded right after the next operation. The following tries to omit that using splitToSequence (Sequences) and groupingBy (see Grouping bottom part):

val result: Map<String, Set<Long>> = input.splitToSequence(',')
    .map { it.split('|', limit = 2) }
    .groupingBy { it[0] }
    .fold({ _, _ -> mutableSetOf<Long>() }) { _, accumulator, element ->
      accumulator.also {
        it.add(element[1].toLong()))
      }
    }

You can of course also filter out the addition of 0 in the set with a simple condition in the fold-step:

// alternative fold skipping 0-values, but keeping keys
.fold({ _, _ -> mutableSetOf<Long>() }) { _, accumulator, element ->
  accumulator.also {
    val value = element[1].toLong()
    if (value != 0L)
      it.add(value)
  }
}

Alternatively also aggregating might be ok, but then your result-variable needs to change to Map<String, MutableSet<Long>>:

val result: Map<String, MutableSet<Long>> = // ...
.aggregate { _, accumulator, element, first ->
  (if (first) mutableSetOf<Long>() else accumulator!!).also {
    val value = element[1].toLong()
    if (value != 0L)
      it.add(value)
  }
}

huangapple
  • 本文由 发表于 2020年6月29日 12:55:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/62631449.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定