根据Kotlin中的条件将列表拆分为连续元素组
我试图根据某种类型和顺序对列表进行分组根据Kotlin中的条件将列表拆分为连续元素组,kotlin,Kotlin,我试图根据某种类型和顺序对列表进行分组 data class Item(val type: Int, val name: String) private fun splitItems(items: List<Item>): List<List<Item>> { val groupedItems = mutableListOf<List<Item>>() var tempList = mutableListOf<I
data class Item(val type: Int, val name: String)
private fun splitItems(items: List<Item>): List<List<Item>> {
val groupedItems = mutableListOf<List<Item>>()
var tempList = mutableListOf<Item>()
items.mapIndexed { index, item ->
if (index > 0) {
val previousItem = items[index - 1]
if (previousItem.type == item.type) {
tempList.add(item)
} else {
if (tempList.isNotEmpty()) groupedItems.add(tempList)
tempList = mutableListOf()
tempList.add(item)
}
} else tempList.add(item)
}
if (tempList.isNotEmpty()) groupedItems.add(tempList)
return groupedItems
}
返回
[Item(type=1, name=Shirt), Item(type=1, name=Shirt)]
[Item(type=2, name=Pant), Item(type=2, name=Pant), Item(type=2, name=Pant)]
[Item(type=1, name=Shirt), Item(type=1, name=Shirt)]
[Item(type=3, name=Tee), Item(type=3, name=Tee)]
[Item(type=2, name=Pant), Item(type=2, name=Pant)]
[Item(type=1, name=Shirt), Item(type=1, name=Shirt), Item(type=1, name=Shirt)]
这是预期的工作。由于我正在尝试学习Kotlin,而且我知道有一种很好的方法可以做到这一点,我想知道如何用Kotlin的方法简化这个逻辑。这是原始解决方案的原始答案。要获得更好的(imo)实施,请滚动至编辑
一些语言,如Kotlin,采用一元函数(T)->U
并返回一个字典,将每个U
映射到映射到它的T
列表,从而实现groupBy
。其他语言,如Haskell,使用(T,T)->布尔
谓词,对满足谓词的连续元素进行分组
Kotlin中没有任何功能可以方便地支持您希望使用的操作。因此,您必须实现自己的。比您的代码略短的代码是:
fun <T> Iterable<T>.groupConsecutiveBy(predicate: (T, T) -> Boolean): List<List<T>> {
var leftToGroup = this.toList()
val groups = mutableListOf<List<T>>()
while (leftToGroup.isNotEmpty()) {
val firstItem = leftToGroup[0]
val newGroup = leftToGroup.takeWhile { predicate(it, firstItem) }
groups.add(newGroup)
leftToGroup = leftToGroup.subList(newGroup.size, leftToGroup.size)
}
return groups
}
这将产生:
在这里,我们对任何T
的Iterable
使用na扩展方法。这是在Kotlin中引入此类功能的惯用方法,因为这是一个由任何Iterable
完成的操作。我还使它成为通用(T
)并接受任何将用连续元素测试的谓词
编辑:实际上,有一个功能可以将每个元素逐个累加到某个结构中。它有时被称为累积、减少,或者在Kotlin中称为折叠:
fun Iterable.groupconsutiveby(groupIdentifier:(T,T)->布尔值)=
如果(!this.any())
空列表()
除此之外
.下降(1)
.fold(mutableListOf(mutableListOf(this.first())){groups,t->
groups.last().apply{
if(groupIdentifier.invoke(last(),t)){
加(t)
}否则{
groups.add(mutableListOf(t))
}
}
组
}
这是一个单一的表达方式,可以说比前一个表达方式更加地道。它不使用原始循环,也不在代码部分之间保持状态。它也很简单,或者所讨论的Iterable
是空的,在这种情况下,我们返回一个空列表,或者,如果存在元素,我们将它们折叠成一个组列表(list
oflist
s)
请注意下拉列表(1)
-之所以这样做,是因为我们将所有元素折叠到已包含该元素的最终列表中(通过折叠()调用中的构造)。通过这样做,我们可以避免引入额外的列表空检查。您应该使用分区
val array = intArrayOf(1, 2, 3, 4, 5)
val (even, odd) = array.partition { it % 2 == 0 }
println(even) // [2, 4]
println(odd) // [1, 3, 5]
使用类型为键、ArrayList为值的HashMap。请问您的使用案例是什么?通常我们会使用groupBy
或类似的实用程序,但它不会在结果中保留“序列”信息。所以我想知道为什么您需要这个数据结构作为输出。实际上,项目是根据时间排序的,我需要保留序列。另一种方法可能会使用。 (对不起,现在没有时间写东西,前面的问题中可能有一些例子。 或者它会成为一个有指导意义的练习:-)这只是为了一分为二。
fun main() {
val items = mutableListOf(
Item(1, "Shirt"),
Item(1, "Shirt"),
Item(2, "Pant"),
Item(2, "Pant"),
Item(2, "Pant"),
Item(1, "Shirt"),
Item(1, "Shirt"),
Item(3, "Tee"),
Item(3, "Tee"),
Item(2, "Pant"),
Item(2, "Pant"),
Item(1, "Shirt"),
Item(1, "Shirt"),
Item(1, "Shirt")
)
items.groupConsecutiveBy{ left, right -> left.type == right.type }.also(::print)
}
[[Item(type=1, name=Shirt), Item(type=1, name=Shirt)], [Item(type=2, name=Pant), Item(type=2, name=Pant), Item(type=2, name=Pant)], [Item(type=1, name=Shirt), Item(type=1, name=Shirt)], [Item(type=3, name=Tee), Item(type=3, name=Tee)], [Item(type=2, name=Pant), Item(type=2, name=Pant)], [Item(type=1, name=Shirt), Item(type=1, name=Shirt), Item(type=1, name=Shirt)]]
fun <T> Iterable<T>.groupConsecutiveBy(groupIdentifier: (T, T) -> Boolean) =
if (!this.any())
emptyList()
else this
.drop(1)
.fold(mutableListOf(mutableListOf(this.first()))) { groups, t ->
groups.last().apply {
if (groupIdentifier.invoke(last(), t)) {
add(t)
} else {
groups.add(mutableListOf(t))
}
}
groups
}
val array = intArrayOf(1, 2, 3, 4, 5)
val (even, odd) = array.partition { it % 2 == 0 }
println(even) // [2, 4]
println(odd) // [1, 3, 5]