如何在科特林中组合两个不同长度的列表?
How to combine two different length lists in kotlin?
我想合并两个不同长度的列表。例如;
val list1 = listOf(1,2,3,4,5)
val list2 = listOf("a","b","c")
我想要这样的结果
(1,"a",2,"b",3,"c",4,5)
有什么建议吗?
您的列表是不可转换的类型(整数和字符串),因此您必须 MutableList<Any>
才能添加这两种类型:
val allItems = mutableListOf<Any>(1,2,3,4,5)
val list2 = listOf("a","b","c")
allItems.addAll(list2)
您可以为此使用 .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()
- 如果源列表中的元素可以在结果列表中以任何顺序出现,则
>>> 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]
你可以这样做:
val mergedList = with(setOf(list1, list2).sortedByDescending { it.count() }) {
first().mapIndexed { index, e ->
listOfNotNull(e, last().getOrNull(index))
}
}.flatten()
首先,将两个列表放在 Set
中,然后按产生列表列表的元素数对其进行排序(降序)。
具有最多元素的第一个列表将用于迭代。
使用 mapIndexed
您可以使用 index
访问第二个列表中的相应元素。如果有none,则返回null
,会被listOfNotNull
过滤掉。最后,您将列表的结果列表展平并获得所需的结果:
[1, a, 2, b, 3, c, 4, 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)
这是我的看法:
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 }
) { }
}
}
我想合并两个不同长度的列表。例如;
val list1 = listOf(1,2,3,4,5)
val list2 = listOf("a","b","c")
我想要这样的结果
(1,"a",2,"b",3,"c",4,5)
有什么建议吗?
您的列表是不可转换的类型(整数和字符串),因此您必须 MutableList<Any>
才能添加这两种类型:
val allItems = mutableListOf<Any>(1,2,3,4,5)
val list2 = listOf("a","b","c")
allItems.addAll(list2)
您可以为此使用 .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()
- 如果源列表中的元素可以在结果列表中以任何顺序出现,则
>>> 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]
你可以这样做:
val mergedList = with(setOf(list1, list2).sortedByDescending { it.count() }) {
first().mapIndexed { index, e ->
listOfNotNull(e, last().getOrNull(index))
}
}.flatten()
首先,将两个列表放在 Set
中,然后按产生列表列表的元素数对其进行排序(降序)。
具有最多元素的第一个列表将用于迭代。
使用 mapIndexed
您可以使用 index
访问第二个列表中的相应元素。如果有none,则返回null
,会被listOfNotNull
过滤掉。最后,您将列表的结果列表展平并获得所需的结果:
[1, a, 2, b, 3, c, 4, 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)
这是我的看法:
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 }
) { }
}
}