根据 Kotlin 中的条件将列表拆分为连续元素组 [英] Split a list into groups of consecutive elements based on a condition in Kotlin

查看:37
本文介绍了根据 Kotlin 中的条件将列表拆分为连续元素组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试根据某种类型以及它们是否按顺序对列表进行分组

data class Item(val type: Int, val name: String)private fun splitItems(items: List): List>{val groupedItems = mutableListOf>()var tempList = mutableListOf()items.mapIndexed { index, item ->如果(索引> 0){val previousItem = items[index - 1]if (previousItem.type == item.type) {tempList.add(item)} 别的 {if (tempList.isNotEmpty()) groupedItems.add(tempList)tempList = mutableListOf()tempList.add(item)}} else tempList.add(item)}if (tempList.isNotEmpty()) groupedItems.add(tempList)返回分组项}

现在这个乐趣将需要

val items = mutableListOf(项目(1,衬衫"),项目(1,衬衫"),项目(2,裤子"),项目(2,裤子"),项目(2,裤子"),项目(1,衬衫"),项目(1,衬衫"),项目(3,三通"),项目(3,三通"),项目(2,裤子"),项目(2,裤子"),项目(1,衬衫"),项目(1,衬衫"),项目(1,衬衫"))

然后返回

[Item(type=1, name=Shirt), Item(type=1, name=Shirt)][项目(类型= 2,名称=裤子),项目(类型= 2,名称=裤子),项目(类型= 2,名称=裤子)][项目(类型= 1,名称=衬衫),项目(类型= 1,名称=衬衫)][项目(类型= 3,名称=三通),项目(类型= 3,名称=三通)][项目(类型= 2,名称=裤子),项目(类型= 2,名称=裤子)][项目(类型= 1,名称=衬衫),项目(类型= 1,名称=衬衫),项目(类型= 1,名称=衬衫)]

这是按预期工作的.由于我正在尝试学习 Kotlin 并且我知道有一种很好的方法可以做到这一点,我想知道如何以 kotlin 的方式简化这个逻辑.

解决方案

这是原始解决方案的原始答案.为了更好的 (imo) 实现,滚动到 EDIT

不同为不同语言的分组功能选择了方法.

有些语言,如 Kotlin,采用一元 函数 (T) -> 实现 groupBy 的方法.U 并返回一个字典,该字典将每个 U 映射到映射到它的 T 列表.其他语言,如 Haskell,使用 (T, T) ->布尔值 谓词将满足谓词的连续元素分组.

Kotlin 中没有任何功能可以方便地支持您想要使用的此类操作.因此,您必须实现自己的.比您的代码略短的代码:

有趣的Iterable.groupConsecutiveBy(predicate: (T, T) -> Boolean): List>{var leftToGroup = this.toList()val 组 = mutableListOf>()而 (leftToGroup.isNotEmpty()) {val firstItem = leftToGroup[0]val newGroup = leftToGroup.takeWhile { 谓词(它,firstItem)}组.添加(新组)leftToGroup = leftToGroup.subList(newGroup.size, leftToGroup.size)}返回组}

并称之为:

有趣的 main() {val 项 = mutableListOf(项目(1,衬衫"),项目(1,衬衫"),项目(2,裤子"),项目(2,裤子"),项目(2,裤子"),项目(1,衬衫"),项目(1,衬衫"),项目(3,三通"),项目(3,三通"),项目(2,裤子"),项目(2,裤子"),项目(1,衬衫"),项目(1,衬衫"),项目(1,衬衫"))items.groupConsecutiveBy{ 左,右 ->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=裤子), 物品(type=2, name=Pant)], [物品(type=1, name=Shirt), 物品(type=1, name=Shirt)], [物品(type=3, name=Tee)), Item(type=3, name=Tee)], [Item(type=2, name=Pant), Item(type=2, name=Pant)], [Item(type=1, name=Shirt),项目(类型= 1,名称=衬衫),项目(类型= 1,名称=衬衫)]]

我们在这里对任何 TIterable 使用 na 扩展方法.这是在 Kotlin 中引入此类功能的惯用方式,因为这是一个操作, 任何 Iterable 都会完成.我还使它成为通用的 (T) 并接受任何将使用连续元素进行测试的 predicate.


实际上,有一个功能可以将每个元素一个一个地累积到某个结构中.它有时被称为 accumulatereduce 或,在 Kotlin 中,fold:

有趣的Iterable.groupConsecutiveBy(groupIdentifier: (T, T) -> Boolean) =如果 (!this.any())空列表()否则这个.drop(1).fold(mutableListOf(mutableListOf(this.first()))) { 组,t ->组.last().应用{if (groupIdentifier.invoke(last(), t)) {添加(t)} 别的 {group.add(mutableListOf(t))}}团体}

这是一个单一的表达方式,可以说比前一个表达方式更加地道.它不使用原始循环并且在代码部分之间不保持状态.这也很简单 - 要么 Iterable 是空的,在这种情况下,我们返回一个空列表,或者,如果元素存在,我们将它们折叠成一个组列表(List的>List).

注意 drop(1) - 我们这样做是因为我们将所有元素折叠到已经包含该元素的最终列表中(通过在 fold() 调用).通过这样做,我们可以避免为列表的空性引入额外的检查.

I am trying to group a list based on some type and if they are in sequence

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
}

Now this fun will take

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")
)

and return

[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)]

This is working as expected. Since I am trying to learn Kotlin and I know there is a beautiful way of doing this, I would like to know how I can simplify this logic in kotlin way.

解决方案

This is the original answer with the original solution. For a better (imo) implementation, scroll to the EDIT

Different approaches are chosen for the grouping functionality by different languages.

Some languages, like Kotlin, take the approach of implementing groupBy by taking a unary function (T) -> U and returning a dictionary that maps every U to a list of Ts that mapped to it. Other languages, like Haskell, work with (T, T) -> Boolean predicates that group consecutive elements that satisfy the predicate.

No functionality in Kotlin supports conveniently such operation that you desire to use. Because of that, you have to implement your own. A slightly shorter code than yours would be:

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
}

and call it like:

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)
}

which would yield:

[[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)]]

We are using na extension method for an Iterable of any T here. This is the idiomatic way of introducing such functionality in Kotlin, because it's an operation that will be done by any Iterable. I also made it generic (T) and to accept any predicate that will be tested with consecutive elements.


EDIT: Actually, there is a functionality that accumulates every single element one by one to a certain structure. It's sometimes called accumulate, reduce or, in 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
        }

This is a single expression which is, arguably, even more idiomatic than the previous one. It uses no raw loops and holds no state in between code parts. It's also very simple - either the Iterable in question is empty and, in that case, we return an empty list, or, in case of elements present, we fold them into a list of groups (List of Lists).

Note the drop(1) - we do that because we fold all elements into the final list that already contains that element (by construction in the fold() call). By doing that we save ourselves from introducing additional checks for the emptiness of the list.

这篇关于根据 Kotlin 中的条件将列表拆分为连续元素组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆