How to combine two different length lists in kotlin?

社会主义新天地 提交于 2020-12-10 00:39:51

问题


I want to combine two different length lists. For example;

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

I want to result like this

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

Is there any suggestion?


回答1:


You may use the .zip function for that

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

The only problem is that it will only process elements, with both sets, so if (like in the example) let's have different size - it will not work

The alternative could be to add specific markers and filter them or to just use iterators for that. I found an elegant solution with sequence{..} function

 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()



回答2:


  1. If the elements from the source lists can occur in any order in the resulting list, then
>>> list1 + list2
res12: kotlin.collections.List<kotlin.Any> = [1, 2, 3, 4, 5, a, b, c]
  1. If the elements from the source lists should alternate in the resulting list and list1 is longer than list2, then
>>> 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]



回答3:


You could do it like this:

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

First, you put both lists in a Set, then you sort it (descending) by the number of elements yielding a list of lists.

The first list has the most elements will be used for iteration.

Using mapIndexed you can use the index to access the corresponding element in the second list. If there is none, null is returned and it will be filtered out by listOfNotNull. In the end you flatten the resulting list of lists and you get the desired result:

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




回答4:


I think Eugenes answer already contains all you need to know to combine two lists (be it zip or combining all elements).

In case you want to combine an arbitrary number of lists, one item per alternating list, you may also be interested in the following approach:

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)
}

Calling it then looks as follows and leads to the value seen in the comment:

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

A simplified approach to the second part of Eugenes answer could be implemented using following code; that, of course, isn't lazy anymore as you get a list back ;-) (but maybe you even translated it directly to a list, so you can also use this approach):

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())
  }
}

Calling it would work as follows:

list1.combine(list2)



回答5:


Here's my take:

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 }
            ) { }
        }
}



回答6:


Your lists are of inconvertible types (Ints and Strings) so you have to have MutableList<Any> so that you can add both types:

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


来源:https://stackoverflow.com/questions/55404428/how-to-combine-two-different-length-lists-in-kotlin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!