#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:
- Если элементы из исходных списков могут располагаться в результирующем списке в любом порядке, то
>>> list1 list2
res12: kotlin.collections.List<kotlin.Any> = [1, 2, 3, 4, 5, a, b, c]
- Если элементы из исходных списков должны чередоваться в результирующем списке и 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. Вы не указываете порядок сортировки. Вы хотите, чтобы элементы чередовались или просто сортировались?