Как объединить два списка разной длины в kotlin?

#java #android #kotlin #kotlin-android-extensions

#java #Android #kotlin #kotlin-android-extensions

Вопрос:

Я хочу объединить два списка разной длины. Например;

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

Я хочу получить такой результат

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

Есть ли какие-либо предложения?

Комментарии:

1. Объедините оба списка как objetc, а затем используйте компаратор kotlinlang.org/api/latest/jvm/stdlib/kotlin/-comparator /…

2. Можете ли вы привести пример?

Ответ №1:

Для этого вы можете использовать .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() amp;amp; second.hasNext()) {
      yield(first.next())
      yield(second.next())
    }

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

Комментарии:

1. Мои списки имеют разный размер, поэтому в моей ситуации это не работает

Ответ №2:

  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]
  

Комментарии:

1. хороший вариант, но он требует, чтобы вы знали, какой список будет больше, чтобы добавить правильный список (из которого вы удалили)

2. True и универсальное решение может быть создано путем изменения второго дополнения на if (list1.size > list2.size) list1.drop(list2.size) else list2.drop(list1.size) .

Ответ №3:

Вы могли бы сделать это следующим образом:

 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]

Ответ №4:

Я думаю, что ответ Евгения уже содержит все, что вам нужно знать, чтобы объединить два списка (будь то 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
  

Упрощенный подход ко второй части ответа Евгения может быть реализован с использованием следующего кода; это, конечно, больше не является ленивым, поскольку вы получаете список обратно 😉 (но, возможно, вы даже перевели его непосредственно в список, так что вы также можете использовать этот подход):

 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)
  

Ответ №5:

Вот мое мнение:

 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:

Ваши списки имеют необратимые типы (целые числа и строки), поэтому у вас должно быть MutableList<Any> , чтобы вы могли добавлять оба типа:

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

Комментарии:

1. Я могу использовать MutableList. это нормально, но в итоге получается 1,2,3,4,5,6, «a», «b», «c» верно? Я хочу, чтобы результат был равен 1, «a», 2, «b», 3, «c», 4, 5

2. Вы не указываете порядок сортировки. Вы хотите, чтобы элементы чередовались или просто сортировались?