当前位置:首页 > 文章列表 > Golang > Go教程 > Go slices.CompactFunc 怎样按业务规则去除相邻重复项

Go slices.CompactFunc 怎样按业务规则去除相邻重复项

来源:17golang原创 2026-10-25 19:26:35 0浏览 收藏

如果旧代码只是把连续出现、且满足某个业务条件的记录压缩成一条,可以把手写循环迁移到 slices.CompactFunc。它不会做全局去重,而是按顺序比较当前保留下来的最后一项与后续元素;比较函数返回 true 时保留这一组的第一项。

官方地址:https://pkg.go.dev/slices#CompactFunc

这次迁移的关键不是把函数名换掉,而是保留原业务语义:只消除相邻重复、比较什么由 eq 决定、结果要接住返回值,而且原切片内容会被原地改写。slices 是 Go 1.21 引入的标准库包;低于这个版本的项目需要先处理工具链升级或继续保留旧实现。

升级范围:从手写循环迁移到标准库

Go 1.21 增加了标准库 slices,其中的 CompactFunc 签名是 func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S。它适合元素不一定支持 ==、但可以由业务函数判断“相等”的切片。

迁移点新行为需要回归的风险
相等判断由 eq(a, b) 决定比较范围不能误删业务字段
去重范围只压缩连续重复运行非相邻相同值仍然保留
内存行为修改原切片并返回较短切片调用方必须使用返回值
尾部处理新长度之外的元素会被清零不能继续把旧长度当有效数据

因此,使用前先确认模块的 go 版本和部署环境。如果旧代码依赖“生成一个全新的切片”或“全局删除相同值”,这就不是等价替换,应先把目标行为写进回归用例。

新写法:让比较函数表达业务规则

下面用事件记录表示一个常见迁移场景:同一渠道中,名称大小写不同但业务上视为同一事件;只有连续出现时才合并,后面的非相邻事件不能被全局删除。

package main

import (
	"fmt"
	"slices"
	"strings"
)

type Event struct {
	Name   string
	Channel string
}

func main() {
	events := []Event{
		{Name: "Login", Channel: "web"},
		{Name: "login", Channel: "web"},
		{Name: "Pay", Channel: "web"},
		{Name: "LOGIN", Channel: "web"},
	}

	// 只把名称忽略大小写且渠道相同的相邻事件视为重复。
	events = slices.CompactFunc(events, func(a, b Event) bool {
		return strings.EqualFold(a.Name, b.Name) && a.Channel == b.Channel
	})

	// CompactFunc 返回压缩后的切片,必须用它更新后续要读取的长度。
	fmt.Println(events)
}
slices.CompactFunc 比较函数、相邻事件和保留首项关系的静态结构说明图
图1:比较规则如何连接事件字段、相邻运行和保留项的结构说明图,不是运行截图。

这个输入最后会留下 Login/web、Pay/web、LOGIN/web 三项。第三项虽然和第一项业务上相等,但它们之间隔着 Pay,所以不会被删除。比较函数越窄,迁移越安全;不要为了“去得更干净”把它改成全局扫描。

旧代码风险:不要漏掉返回值和原地语义

手写循环常常同时维护读写下标,迁移时最容易漏掉两件事:第一,CompactFunc 返回的是新长度对应的切片;第二,它可能复用原底层数组。只调用函数但继续读取原来的 len(events),会把已经不属于结果的尾部当成有效记录。

func compactEvents(events []Event) []Event {
	// 空切片直接返回,保留 nil 切片的语义。
	if len(events) == 0 {
		return events
	}

	// 返回值代表有效范围;函数内部会清理新长度之外的元素。
	return slices.CompactFunc(events, func(a, b Event) bool {
		return strings.EqualFold(a.Name, b.Name) && a.Channel == b.Channel
	})
}

如果切片元素包含指针、映射或较大的对象引用,尾部清零尤其值得注意:结果之外的引用不会继续挂在底层数组上。若结果需要长期独立保存,还要按业务需要再做复制或容量裁剪;CompactFunc 本身解决的是相邻压缩,不自动替你完成所有生命周期设计。

回归检查:相邻重复、nil 和业务边界

升级后至少检查四类输入。连续相同项应该只保留第一项;非相邻相同项必须继续存在;空切片和 nil 切片不能被业务代码误判;比较函数中的字段范围则要覆盖大小写、渠道、租户或状态等真正影响“相同”的条件。

func sameEvent(a, b Event) bool {
	// 只比较业务定义的身份字段,不把展示字段误当成去重条件。
	return strings.EqualFold(a.Name, b.Name) && a.Channel == b.Channel
}

func compactForTest(events []Event) []Event {
	// 统一从一个入口调用,方便测试结果长度和原地修改后的内容。
	return slices.CompactFunc(events, sameEvent)
}
slices.CompactFunc 相邻重复、非相邻重复、nil 切片和尾部清零边界关系图
图2:相邻压缩与非相邻保留、nil 语义、尾部清零之间的边界关系说明图,不是运行截图。
检查项应观察到的结果
连续三条业务相等记录只保留第一条,长度减少两项
相等记录被其他事件隔开两条都保留,不做全局去重
nil 切片结果仍保持 nil 语义
指针或大对象尾部新长度之外的元素被清零

迁移清单和常见追问

迁移可以按这个顺序完成:确认项目使用 Go 1.21 或更高版本;把原循环的“相同”条件提取成 eq;明确只处理相邻运行;接住返回切片;补上连续、非相邻、nil 和尾部引用测试。完成后再删除旧循环,避免两套实现同时改变业务。

CompactFunc 会删除所有相同元素吗?不会,它只压缩连续且被 eq 判定为相同的一段。

为什么一定要接收返回值?返回切片的长度变短了,后续遍历必须使用新长度。

比较函数可以只比较一个字段吗?可以,但那个字段必须确实代表业务身份;否则会把不同租户、渠道或状态的记录误合并。

把 slices.CompactFunc 当作“带业务比较器的相邻压缩”最稳妥:它能替代重复的读写下标代码,但不会替你决定全局去重、字段身份或对象生命周期。迁移时守住这三个边界,标准库写法才会和旧业务行为保持一致。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go strings.IndexRune 怎样按 Unicode 字符定位首个符文Go strings.IndexRune 怎样按 Unicode 字符定位首个符文
上一篇
Go strings.IndexRune 怎样按 Unicode 字符定位首个符文
Go slices.IndexFunc 怎样从切片中定位首个有效值
下一篇
Go slices.IndexFunc 怎样从切片中定位首个有效值
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    689次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    702次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    762次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    725次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    561次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码