我正在尝试根据某种类型以及它们是否按顺序对列表进行分组
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
}
Run Code Online (Sandbox Code Playgroud)
现在这个乐趣将需要
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")
)
Run Code Online (Sandbox Code Playgroud)
并返回
[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)]
Run Code Online (Sandbox Code Playgroud)
这按预期工作。因为我正在尝试学习 Kotlin,并且我知道有一种很好的方法可以做到这一点,所以我想知道如何以 kotlin 的方式简化这个逻辑。
这是原始答案和原始解决方案。为了更好的(imo)实现,滚动到编辑
某些语言(例如 Kotlin)采用的实现方法groupBy
是采用一元函数 (T) -> U
并返回一个字典,该字典将 every 映射到映射到它的 sU
列表。T
其他语言(例如 Haskell)使用(T, T) -> Boolean
谓词对满足谓词的连续元素进行分组。
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
}
Run Code Online (Sandbox Code Playgroud)
并这样称呼它:
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)
}
Run Code Online (Sandbox Code Playgroud)
这会产生:
Run Code Online (Sandbox Code Playgroud)[[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)]]
我们在这里使用任何一个扩展Iterable
方法T
。这是在 Kotlin 中引入此类功能的惯用方式,因为这是任何Iterable
. 我还将其设为通用 ( T
) 并接受任何predicate
将使用连续元素进行测试的内容。
编辑:实际上,有一种功能可以将每个元素逐一累积到某种结构。它有时被称为“accumulate”、“reduce”,或者在 Kotlin 中称为fold
:
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
}
Run Code Online (Sandbox Code Playgroud)
这是一个单一的表达方式,可以说比前一个表达方式更惯用。它不使用原始循环,并且在代码部分之间不保留任何状态。它也非常简单 - 要么Iterable
有问题的是空的,在这种情况下,我们返回一个空列表,或者,如果存在元素,我们将它们折叠成一个组列表(List
s List
)。
请注意drop(1)
- 我们这样做是因为我们将fold
所有元素放入已包含该元素的最终列表中(通过调用中的构造fold()
)。通过这样做,我们可以避免引入额外的检查列表是否为空的过程。
归档时间: |
|
查看次数: |
4611 次 |
最近记录: |