登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go slices.CompactFunc 如何处理相邻重复结构体

来源:17golang原创

时间:2026-09-15 04:29:39 245浏览 收藏

处理结构体切片里的重复记录时,slices.CompactFunc 适合解决“相邻记录按某个字段相等,只保留第一条”的问题。它不会新建一份切片,也不会把分散在不同位置的记录合并;调用后要接住返回值,并把比较规则明确写在 eq 函数里。

要点速览
  • CompactFunc 只压缩连续相等的一段,结构体通常按业务键比较。
  • 每段连续重复数据保留第一条,原切片的底层数组会被就地改写。
  • 返回值代表新的长度视图;旧长度范围的尾部元素会被清零。

先把相邻重复的判断写对

假设导入记录已经按业务顺序排好,同一个 Key 可能连续出现多次。结构体包含其他展示字段时,直接比较整个结构体往往过于严格,因为名称或时间可能不同。更稳妥的做法是只比较真正决定“同一条记录”的字段。

package main

import (
    "fmt"
    "slices"
)

type Record struct {
    Key  string
    Name string
}

func main() {
    records := []Record{
        {Key: "go", Name: "Go 入门"},
        {Key: "go", Name: "Go 基础"},
        {Key: "db", Name: "数据库"},
        {Key: "go", Name: "Go 实战"},
    }

    // 只按 Key 判断相邻记录是否属于同一组。
    records = slices.CompactFunc(records, func(a, b Record) bool {
        return a.Key == b.Key
    })

    // 读取返回值后的新长度,避免继续使用旧的尾部数据。
    fmt.Println(records)
}

这个比较函数把前两条 go 记录视为相等,所以输出保留第一条“Go 入门”;后面的 godb 不相邻,不会被前面的 go 组吞掉。按这个输入推导出的结果是:

[{go Go 入门} {db 数据库} {go Go 实战}]
Record 字段、eq 比较函数、slices.CompactFunc 与保留首元素的静态关系示意图
图1:结构体字段通过 eq 比较函数连接到 CompactFunc,结果侧保留每段相邻相等数据的首元素;这是静态关系示意图。

CompactFunc 只处理连续重复

标题里的“相邻”是这个 API 最容易被忽略的边界。输入 [go, go, db, go] 会变成 [go, db, go],不会变成只有一条 go。如果需求是全局去重,需要先按键排序,或另外维护一个已见键集合;不要把两种语义混在一个比较函数里。

比较函数接收一对元素,而不是接收整段切片。它最好只表达稳定的等价关系:同样的键返回 true,不同的键返回 false。如果比较规则依赖随机数、时间或可变外部状态,同一批数据的结果就可能不稳定。

输入特征CompactFunc 的处理需要采用的方案
相邻 Key 相同保留连续段的第一条直接使用 CompactFunc
相同 Key 分散出现分别保留,顺序不变先排序或维护集合
整条结构体完全相等可用结构体比较确认字段都属于等价规则

就地修改与尾部清零

CompactFunc 返回的是同一类切片类型,但长度可能变短。标准库会把保留下来的元素向前写回,并把新长度之后的元素清零;对包含指针、切片、映射或接口的结构体,这能避免旧尾部继续持有引用。

因此不要只调用函数而忽略返回值:

// 错误示例:底层内容可能已变化,但 records 的长度仍是旧长度。
slices.CompactFunc(records, equalRecord)

// 正确示例:用返回值更新长度视图。
records = slices.CompactFunc(records, equalRecord)

如果后续逻辑还要追加数据,可以继续使用返回后的切片;如果业务代码需要保留原始输入,就先用 slices.Clone 复制,再对副本做压缩。

输入切片、底层数组、相邻扫描、写回游标、结果切片和尾部零值的静态关系示意图
图2:输入切片与底层数组共享存储关系,写回游标形成结果切片,旧尾部归零;这是内存关系示意图。

把按 Key 去重封装成小函数

当同一规则在导入、分页合并和缓存整理中反复出现时,可以把字段比较留在业务函数里,让调用处只看到“按键压缩”:

func compactRecords(records []Record) []Record {
    // 返回新长度视图,调用者不需要知道底层写回细节。
    return slices.CompactFunc(records, func(a, b Record) bool {
        return a.Key == b.Key
    })
}

封装前先确认调用者接受“只去相邻重复”的语义,并给空切片、单元素切片、全重复和重复分散四种输入补上测试。这样测试检查的是业务边界,而不只是 API 是否能编译。

相关问题

CompactFunc 会排序结构体切片吗?

不会。它保持原有顺序,只压缩相邻且被 eq 判定为相等的连续段。

为什么比较器按 Key 判断,而不是比较整个结构体?

因为名称、时间等字段可能是展示信息;是否属于同一组应由明确的业务键决定。

调用 CompactFunc 后还要保留原切片怎么办?

先调用 slices.Clone 得到副本,再把副本传给 CompactFunc,原切片就不会被这次就地修改影响。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>