You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Kotlin扩展函数将多表INNER JOIN结果转换为三级嵌套列表

如何使用Kotlin扩展函数将多表INNER JOIN结果转换为三级嵌套列表

嗨,我完全懂你现在的困扰——从SQL多表联查得到的扁平结果里,手动构建三级嵌套的Category/Feature/Schedule结构确实容易绕晕,而且你之前用collect加Map的方式很容易出错。咱们可以用Kotlin的集合扩展函数来优雅解决这个问题,代码简洁还不容易出错。

首先先明确咱们的输入结构(就是你从数据库拿到的扁平结果类):

// 数据库联查返回的扁平结果类
data class Result(
    val categoryId: Long,
    val caption: String,
    val featureId: Long,
    val name: String,
    val rotation: String,
    val scheduleId: Long,
    val time: String
)

接下来直接给你写核心的扩展函数,专门用来把扁平列表转换成你需要的三级嵌套结构:

// 为List<Result>编写扩展函数,一键转换为三级嵌套列表
fun List<Result>.toNestedCategories(): List<Category> {
    // 第一步:先按分类ID分组,把同分类的所有数据归到一起
    return this.groupBy { it.categoryId }
        .map { (categoryId, categoryRelatedResults) ->
            // 第二步:对当前分类下的所有数据,再按功能ID分组
            val features = categoryRelatedResults.groupBy { it.featureId }
                .map { (featureId, featureRelatedResults) ->
                    // 第三步:把当前功能下的所有记录转换成Schedule列表
                    val schedules = featureRelatedResults.map {
                        Schedule(id = it.scheduleId, time = it.time)
                    }
                    // 组装Feature对象,用first()取重复的name/rotation(同功能下这些值是一致的)
                    Feature(
                        id = featureId,
                        name = featureRelatedResults.first().name,
                        rotation = featureRelatedResults.first().rotation,
                        schedules = schedules
                    )
                }
            // 组装最终的Category对象,同样用first()取重复的caption
            Category(
                id = categoryId,
                caption = categoryRelatedResults.first().caption,
                features = features
            )
        }
}

函数逻辑解释

  1. 第一层分组:用groupBy { it.categoryId }把所有扁平结果按分类ID拆分,确保每个分组里的都是同一个分类下的关联数据
  2. 第二层分组:对每个分类分组,再用groupBy { it.featureId }拆分出该分类下的所有功能组
  3. 生成日程列表:每个功能组里的每条记录对应一个日程,用map直接转换成Schedule对象
  4. 组装嵌套对象:分别把日程列表组装成Feature,再把Feature列表组装成Category,因为同分组里的caption/name/rotation都是重复值,用first()取第一个就够了,安全又高效

使用示例

如果是普通列表直接调用:

// 假设这是你从数据库拿到的扁平结果列表
val flatDbResults = listOf(
    Result(1, "kategorie1", 1, "A1", "0", 10, "10 AM"),
    Result(1, "kategorie1", 1, "A1", "0", 11, "12 AM"),
    Result(1, "kategorie1", 2, "A2", "0", 12, "10 AM"),
    Result(1, "kategorie1", 2, "A2", "0", 13, "12 AM"),
    Result(2, "kategorie2", 3, "B1", "0", 21, "10 AM"),
    Result(2, "kategorie2", 4, "B2", "0", 23, "10 AM"),
    Result(2, "kategorie2", 4, "B2", "0", 25, "12 AM")
)

// 调用扩展函数完成转换
val nestedCategories = flatDbResults.toNestedCategories()

如果是用Coroutine Flow的场景,直接在map操作符里调用就行:

// 假设这是你从数据库获取的Flow<List<Result>>
val dbResultFlow: Flow<List<Result>> = // 你的数据库查询Flow

dbResultFlow
    .map { it.toNestedCategories() }
    .collect { nestedList ->
        // 这里就可以拿到转换好的三级嵌套列表,直接返回给API就行
        println("转换后的嵌套结构:$nestedList")
    }

注意点

  • 确保你的Result类里的字段和数据库查询结果完全对应,尤其是categoryId/featureId这些外键字段不能漏
  • 用first()取重复字段是安全的,因为同分组里的这些值都是来自同一个分类/功能,不会有数据不一致的问题
  • 这个扩展函数是纯函数,不会修改原列表,完全符合Kotlin函数式编程的风格

备注:内容来源于stack exchange,提问作者ajeshbaby

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 08:39:31