【问题标题】:How to combine two different length lists in kotlin?如何在kotlin中组合两个不同长度的列表?
【发布时间】:2019-08-19 15:03:11
【问题描述】:

我想组合两个不同长度的列表。例如;

val list1 = listOf(1,2,3,4,5)
val list2 = listOf("a","b","c")

我想要这样的结果

(1,"a",2,"b",3,"c",4,5)

有什么建议吗?

【问题讨论】:

标签: java android kotlin kotlin-android-extensions


【解决方案1】:

您的列表是不可转换的类型(整数和字符串),因此您必须拥有 MutableList<Any> 以便您可以添加两种类型:

val allItems = mutableListOf<Any>(1,2,3,4,5)
val list2 = listOf("a","b","c")
allItems.addAll(list2)

【讨论】:

  • 我可以使用 mutableList。没关系,但是 addAll 结果是 1,2,3,4,5,6,"a","b","c" 对吗?我希望结果是 1,"a",2,"b",3,"c",4,5
  • 您没有指定排序顺序。您希望项目交替进行还是仅进行排序?
【解决方案2】:

您可以为此使用.zip 函数

list1.zip(list2){ a,b -> listOf(a,b)}.flatten()

唯一的问题是它只会处理两个集合的元素,所以如果(如示例中)让我们有不同的大小 - 它将不起作用

替代方法可以是添加特定标记并过滤它们,或者只使用迭代器。我用sequence{..} 函数找到了一个优雅的解决方案

 val result = sequence {
    val first = list1.iterator()
    val second = list2.iterator()
    while (first.hasNext() && second.hasNext()) {
      yield(first.next())
      yield(second.next())
    }

    yieldAll(first)
    yieldAll(second)
  }.toList()

【讨论】:

  • 我的列表大小不同,因此不适用于我的情况
【解决方案3】:
  1. 如果源列表中的元素可以在结果列表中以任意顺序出现,则
>>> list1 + list2
res12: kotlin.collections.List<kotlin.Any> = [1, 2, 3, 4, 5, a, b, c]
  1. 如果源列表中的元素应该在结果列表中交替出现并且 list1 比 list2 长,那么
>>> list1.zip(list2).flatMap { listOf(it.first, it.second) } + list1.drop(list2.size)
res16: kotlin.collections.List<kotlin.Any> = [1, a, 2, b, 3, c, 4, 5]

【讨论】:

  • 不错,但它需要您知道哪个列表更大,以便附加正确的列表(您从中删除)
  • 正确,可以通过将第二个加数更改为 if (list1.size &gt; list2.size) list1.drop(list2.size) else list2.drop(list1.size) 来创建通用解决方案。
【解决方案4】:

你可以这样做:

val mergedList = with(setOf(list1, list2).sortedByDescending { it.count() }) {
    first().mapIndexed { index, e ->
        listOfNotNull(e, last().getOrNull(index))
    }
}.flatten()

首先,将两个列表放入Set,然后按元素数量对其进行排序(降序),生成列表列表。

第一个具有最多元素的列表将用于迭代。

使用mapIndexed,您可以使用index 访问第二个列表中的相应元素。如果没有,则返回null,并被listOfNotNull 过滤掉。最后,您将生成的列表列表展平,并获得所需的结果:

[1, a, 2, b, 3, c, 4, 5]

【讨论】:

    【解决方案5】:

    我认为 Eugenes 的答案已经包含了组合两个列表所需的所有知识(无论是 zip 还是组合所有元素)。

    如果您想组合任意数量的列表,每个交替列表一个项目,您可能还对以下方法感兴趣:

    fun combine(vararg lists: List<*>) : List<Any> = mutableListOf<Any>().also {
      combine(it, lists.map(List<*>::iterator))
    }
    
    private tailrec fun combine(targetList: MutableList<Any>, iterators: List<Iterator<*>>) {
      iterators.asSequence()
              .filter(Iterator<*>::hasNext)
              .mapNotNull(Iterator<*>::next)
              .forEach { targetList += it }
      if (iterators.asSequence().any(Iterator<*>::hasNext))
        combine(targetList, iterators)
    }
    

    然后调用它看起来如下并导致评论中看到的值:

    combine(list1, list2) // List containing: 1, "a", 2, "b", 3, "c", 4, 5
    combine(list1, list2, listOf("hello", "world")) // 1, "a", "hello", 2, "b", "world", 3, "c", 4, 5
    

    可以使用以下代码实现 Eugenes 答案第二部分的简化方法;当然,当你得到一个列表时,它不再懒惰了;-)(但也许你甚至将它直接翻译成一个列表,所以你也可以使用这种方法):

    fun List<Any>.combine(other: List<Any>) : List<Any> = mutableListOf<Any>().also {
      val first = iterator()
      val second = other.iterator()
      while (first.hasNext() || second.hasNext()) {
        if (first.hasNext()) it.add(first.next())
        if (second.hasNext()) it.add(second.next())
      }
    }
    

    调用它的工作方式如下:

    list1.combine(list2)
    

    【讨论】:

      【解决方案6】:

      这是我的看法:

      fun <T> zip(vararg iterables: Iterable<T>): List<T> = iterables
      .map { it.iterator() }
      .toList()
      .let { iterators ->
          mutableListOf<T>()
              .also { list ->
                  while (
                    iterators.any {if (it.hasNext()) list.add(it.next()) else false }
                  ) { }
              }
      }
      

      【讨论】:

        猜你喜欢
        • 2019-10-16
        • 1970-01-01
        • 2017-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多