【问题标题】:Kotlin combining 'N' lists together by summing each elementKotlin 通过对每个元素求和来组合“N”个列表
【发布时间】:2019-10-20 17:56:58
【问题描述】:

我试图通过将每个单独的元素加在一起并输出一个带有结果的最终列表来将 N 个列表组合在一起。这是一个可视化:

List 1: { 1, 2, 3 }
List 2: { 4, 5, 6 }
List 3: { 7, 8, 9 }
...
List N: { X, X, X }

如果我们结合前三个,输出将是:

List Result: { 12, 15, 18 }

我想继续对每个索引处的元素求和,干净、内联,并具有最佳性能。

我知道 zip 运算符在这里会有所帮助(很容易做到 list1.zip(list2) 并添加 pair.first 和 pair.second),但它不能处理多个列表。

我自学了 Kotlin 集合操作以尝试找到解决方案 - 我研究了使用 fold、reduce、flatMap、zip、groupBy,并可能将 List 转换为序列以利用每个元素方面的优势。但我似乎无法找到一种干净的方法将这些操作链接在一起以获得令人满意的结果。

附加信息:

我现在拥有的是一个名为 padStart(为简洁起见隐藏)的扩展方法,以帮助确保我所有的嵌套列表的长度相同,然后我笨拙地创建了一个临时列表,我在迭代时添加了值:

myNestedLists
    .run {
        // Calculate largest list size of 'n' lists
        val largestSize = map { it.size }.max() ?: 0 
        // Pad lists with zeroes if size < largestSize
        map { it.padStart(largestSize, 0.0) }
    }
    .run {
        // Create temporary list, add all of the first elements to it
        val combinedList: MutableList<Double> = mutableListOf()

        combinedList.addAll(this.first())

        // Now, for each nested list, add each individual element with the combined value at combinedList[index]
        drop(1).forEach {
            it.forEachIndexed { index, value ->
                combinedList[index] += value
            }
        }

        // Return the final result
        combinedList
    }

此解决方案有效,但难以阅读且不是很干净。我正在寻找更好的!

【问题讨论】:

    标签: kotlin collections


    【解决方案1】:

    功能性和直接性:

    lists.maxBy { it.size }!!.indices
         .map { index ->
            lists.mapNotNull { it.getOrNull(index) }.sum()
         }
    

    如果您关心性能,编译器无论如何都会对其进行优化。

    编辑:

    如果列表的数量很大或者值是由服务获取的,您可以在 map 操作中使用协程。

    【讨论】:

      【解决方案2】:

      正如您所观察到的那样,zip 在两个列表中都能很好地完成这项工作。如果我们使用fold,那么我们可以使用zip 将每个列表与一个累加器值组合起来,并对嵌套列表中的每个列表重复此操作。

      如果我们可以假设所有列表都是三个元素(根据最初的问题),我们可以这样做:(免责声明:最低限度的测试,使用风险自负:))

      fun sumList(xs: List<List<Int>>): List<Int> =
              xs.fold(listOf(0, 0, 0)) { x, y ->
                  x.zip(y, Int::plus)
              }
      

      对于可变长度,我们可以填充列表,或者创建一个专门的 zip 函数来处理不同长度的 int 列表(相同的免责声明:)):

      fun List<Int>.padTo(requiredSize: Int) =
              if (size < requiredSize) {
                  plus(generateSequence { 0 }.take(requiredSize - size))
              } else {
                  this
              }
      
      fun sumVariableLists(xs: List<List<Int>>): List<Int> =
              xs.fold(emptyList()) { x, y ->
                  val maxSize = maxOf(x.size, y.size)
                  x.padTo(maxSize).zip(y.padTo(maxSize), Int::plus)
              }
      
      @Test
      fun example() {
          val x = listOf(
                  listOf(1, 2, 3, 10),
                  listOf(4, 5, 6),
                  listOf(7, 8, 9, 11))
      
          assertEquals(listOf(12, 15, 18, 21), sumVariableLists(x))
      }
      

      在性能方面,您可能最好使用可变列表和普通的旧循环来增加每个值,但我认为 foldzip 满足清洁度要求。 :)

      【讨论】:

      • 发布的每个解决方案似乎都有效,但是根据问题,这个(结合我自己的填充方法)最终成为最快速和最简洁的解决方案。谢谢!
      【解决方案3】:

      我会做类似以下的事情,

      fun getResult(nestedList: List<List<Int>>) : List<Int> {
          val list = mutableListOf<Int>()
          val maxSize = nestedList.maxBy{it.size}?.size ?: 0
      
          repeat(maxSize) { i ->
              val sum = nestedList.sumBy{if(i<it.size) it[i] else 0}
              list.add(sum)
          }
          return list
      }
      

      【讨论】:

        【解决方案4】:

        “..然后我笨拙地创建了一个临时列表..” 它并不笨拙

        https://en.wikipedia.org/wiki/KISS_principle 在这种情况下,只需“保持简单,愚蠢”

         fun <T> sumLists(vararg lists: List<T>, sum: (a: T, b: T) -> T): List<T> {
            val result = ArrayList<T>(lists.first())
            lists.drop(1).forEach {l->
                l.forEachIndexed {i,v->
                    result[i] = sum(result[i], v)
                }
            }
            return result.toList()
        }
        
        fun main(args: Array<String>) {
            val l1 = listOf(1, 2, 3)
            val l2 = listOf(4, 5, 6)
            val l3 = listOf(7, 8, 9)
            print(sumLists(l1,l2,l3,sum = Int::plus))
        
        }
        

        【讨论】:

          猜你喜欢
          • 2019-04-14
          • 1970-01-01
          • 2020-08-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-21
          • 1970-01-01
          相关资源
          最近更新 更多