深入理解 lo 迭代器包中的 Reject 系列函数:Filter 的反向序列过滤与映射

发布时间:2026/9/13 14:52:45

深入理解 lo 迭代器包中的 Reject 系列函数:Filter 的反向序列过滤与映射 深入理解 lo 迭代器包中的 Reject 系列函数Filter 的反向序列过滤与映射【免费下载链接】lo A Lodash-style Go library based on Go 1.18 Generics (map, filter, contains, find...)项目地址: https://gitcode.com/GitHub_Trending/lo/lo导读本文聚焦 samber/lo 迭代器子包it包位于仓库 it/ 目录中的Reject、RejectI、RejectMap与RejectMapI四个序列操作函数。它们是Filter系列的反向操作保留谓词返回 false 的元素同时具备惰性求值、支持提前终止、保留自定义序列类型等特性。读完本文你将掌握这组函数在 Go 1.23iter.Seq序列上的全部用法、底层实现原理以及它们在仓库内部如WithoutBy、WithoutNth、DropByIndex的真实组合场景。一、Reject 与 Filter一枚硬币的两面在 docs/data/it-reject.md 的定位中Reject被明确定义为Filter 的反向操作Reject is the opposite of FilterFilter保留谓词返回 true 的元素而Reject保留谓词返回 false 的元素。这一语义与 JavaScript Lodash 的reject完全一致也与仓库中 slice 版本的 core-reject.md 呼应——两者都返回谓词为 false 的元素。该文档登记了四个变体函数全部位于it包category:itersubCategory:sequence构成完整的反向过滤工具族函数签名特性Rejectfunc RejectT any, I ~func(func(T) bool) bool) I按元素值过滤RejectIfunc RejectIT any, I ~func(func(T) bool) bool) I谓词携带索引RejectMapfunc RejectMapT, R any (R, bool)) iter.Seq[R]过滤 类型转换映射RejectMapIfunc RejectMapIT, R any (R, bool)) iter.Seq[R]过滤 映射 索引二、Reject基础的反向过滤2.1 用法Reject遍历序列仅产出predicate返回 false 的元素并保持原始相对顺序collection : func(yield func(int) bool) { yield(1) yield(2) yield(3) yield(4) } filtered : it.Reject(collection, func(x int) bool { return x%2 0 }) var result []int for item : range filtered { result append(result, item) } // result contains [1, 3]这段示例出自 docs/data/it-reject.mdit包的正式示例测试 it/seq_example_test.go 也给出了等价的可运行验证func ExampleReject() { list : slices.Values([]int{0, 1, 2, 3, 4, 5}) result : Reject(list, func(x int) bool { return x%2 0 }) fmt.Printf(%v, slices.Collect(result)) // Output: [1 3 5] }注意ExampleReject用slices.Values将普通切片转换为iter.Seq这正是it包的设计初衷——所有函数面向Go 1.23 的iter序列工作源码文件头部有//go:build go1.23构建约束见 it/seq.go。2.2 源码实现// Reject is the opposite of Filter, this method returns the elements of collection that predicate does not return true for. func RejectT any, I ~func(func(T) bool) bool) I { return RejectI(collection, func(item T, _ int) bool { return predicate(item) }) }对应源码见 it/seq.go。可以看到Reject只是RejectI的薄封装它丢弃索引参数直接委托给RejectI。这与Filter/FilterI、Map/MapI的架构完全一致——整个it包以带索引的基础实现 无索引便捷封装为统一模式。三、RejectI带索引的反向过滤3.1 用法RejectI的谓词额外接收元素索引适合需要按位置过滤的场景collection : func(yield func(string) bool) { yield(a) yield(b) yield(c) } filtered : it.RejectI(collection, func(item string, index int) bool { return index 1 }) var result []string for item : range filtered { result append(result, item) } // result contains [a, c]3.2 源码实现与惰性求值func RejectIT any, I ~func(func(T) bool) bool) I { return func(yield func(T) bool) { var i int for item : range collection { if !predicate(item, i) !yield(item) { return } i } } }对应源码见 it/seq.go这段实现包含两个关键设计惰性求值RejectI返回的是一个新的迭代函数闭包而不是立即遍历并物化结果。只有当消费者在for ... range中拉取元素时谓词才会被执行。这意味着它可以串联在无限序列或流式管线上使用。提前终止传播注意!yield(item)的判断——当消费者下游返回 false 表示提前停止时RejectI会立刻return停止继续消费上游从而将提前终止信号沿管线向上游传播避免不必要的计算。3.3 泛型约束类型保留I ~func(func(T) bool)是一个近似类型约束underlying type 为函数类型它允许传入任何以func(func(T) bool)为底层类型的自定义序列类型且返回类型I会保留该自定义类型。测试 it/seq_test.go 专门验证了这一点type myStrings iter.Seq[string] allStrings : myStrings(values(, foo, bar)) nonempty : RejectI(allStrings, func(x string, _ int) bool { return len(x) 0 }) is.IsType(nonempty, allStrings, type preserved)同样Reject的测试也覆盖了该行为it/seq_test.go并验证了元素顺序保持如{Smith,foo,Domin,bar,Olivia}过滤掉长度 3 的名字后得到[foo, bar]见 it/seq_test.go。四、RejectMap 与 RejectMapI过滤与映射的融合4.1 用法RejectMap在一次遍历中同时完成反向过滤和类型转换映射是FilterMap的反向版本。回调函数返回两个值映射操作的结果以及该结果元素是否应被包含true 包含false 排除collection : func(yield func(int) bool) { yield(1) yield(2) yield(3) yield(4) } filtered : it.RejectMap(collection, func(x int) (string, bool) { return fmt.Sprintf(item-%d, x), x%2 0 }) var result []string for item : range filtered { result append(result, item) } // result contains [item-1, item-3]在这个例子中偶数元素的第二个返回值是 true应包含但RejectMap的语义与FilterMap相反只有第二个返回值为 false 的元素才会被保留因此最终得到item-1和item-3。4.2 源码实现RejectMap委托给RejectMapIit/seq.go核心实现如下it/seq.gofunc RejectMapIT, R any (R, bool)) iter.Seq[R] { return func(yield func(R) bool) { var i int for item : range collection { if r, ok : callback(item, i); !ok !yield(r) { return } i } } }关键点在!ok !yield(r)这一行!ok回调第二个返回值 ok 为 false 时才继续实现反向包含 !yield(r)一旦消费者提前终止立即返回。注意这里与RejectI的差异RejectMapI返回类型是iter.Seq[R]输出元素类型R可以与输入T不同因此映射过程顺带完成了类型转换如示例中int → string。4.3 测试验证测试 it/seq_test.go 覆盖了数值与字符串两类场景例如r1 : RejectMap(valuesint64, func(x int64) (string, bool) { if x%2 0 { return strconv.FormatInt(x, 10), false } return , true }) is.Equal([]string{2, 4}, slices.Collect(r1))偶数的回调返回(2, false)——即被保留所以输出为[2, 4]。字符串用例cpu, gpu, mouse, keyboard后缀为pu的映射为xpu并保留验证了RejectMap的正确性。同时RejectI与RejectMapI的测试都调用了assertSeqSupportBreak辅助函数见 it/seq_test.go进一步确认了提前终止语义在带索引变体中同样生效。五、底层原理与设计要点总结综合 it/seq.go 的四个实现可以归纳出该函数族的设计模式函数即序列所有返回的I/iter.Seq[R]本质都是func(yield func(T) bool)形式的拉取式迭代器遵循 Go 1.23iter.Seq约定天然支持for range。封装金字塔Reject → RejectI、RejectMap → RejectMapI无索引版本一律通过丢弃索引参数委托给带索引版本避免逻辑重复。单遍扫描与 slice 版lo.Reject的预分配切片实现见 slice.goresult : make(Slice, 0, len(collection))不同it版不物化结果不产生中间切片适合流式处理。标准库互操作输入输出均可直接与slices.Collect、slices.Values等标准库函数衔接实现切片 ↔ 序列的平滑转换。六、仓库内部的真实组合场景Reject并非孤立函数它被仓库其他迭代器操作复用来构建更高级的语义WithoutByit/intersect.go先通过lo.Keyify构造排除键集合再用Reject剔除键命中排除集的元素func WithoutByT any, K comparable, I ~func(func(T) bool) K, exclude ...K) I { set : lo.Keyify(exclude) return Reject(collection, func(item T) bool { return lo.HasKey(set, transform(item)) }) }WithoutNthit/intersect.go用RejectI按索引剔除指定位置的元素本质是按位置反向过滤return RejectI(collection, func(_ T, index int) bool { return lo.HasKey(set, index) })DropByIndexit/seq.go同样委托给RejectI通过索引集合实现任意位置批量删除。这三处复用证明Reject/RejectI是it包中排除类操作without/drop的底层基础设施理解了它们就等于理解了整个排除族函数的实现骨架。七、与 slice 版本 lo.Reject 的对照如果你更熟悉主包的切片 API可以对比 docs/data/core-reject.md 中的签名func Reject[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) Slice差异要点维度lo.Rejectcore/sliceit.Rejectiter/sequence输入/输出~[]T切片iter.Seq/ 自定义序列I求值时机立即返回新切片惰性消费时才执行谓词签名始终带(item, index)Reject无索引RejectI带索引额外变体RejectErr错误中断、FilterReject双路分区RejectMap/RejectMapI映射融合选择建议处理内存中的小切片、需要错误处理或一次性分区时用 core 版处理流式数据、自定义序列类型、需要管道串联与提前终止时用it版。两者的语义保留谓词为 false 的元素完全一致可放心切换。八、注意事项与最佳实践谓词语义容易混淆Reject保留的是谓词返回false的元素而RejectMap中保留的是回调第二个返回值ok为false的元素。两个false的判定方向是一致的都是反向包含阅读代码时留意即可。惰性带来的副作用时机谓词在for range消费时才执行不要假设调用Reject的瞬间谓词已被调用若谓词有副作用如日志、计数其触发顺序由消费者的拉取节奏决定。空序列与全排除输入为空序列时返回的迭代器直接不产出任何元素这是迭代器函数的自然行为无需特殊处理。结合slices.Collect物化需要普通切片结果时用slices.Collect(it.Reject(seq, pred))一行完成转换这正是 it/seq_example_test.go 的惯用写法。提前终止是性能特性当Reject处于长管道中间位置时下游一旦停止如it.Take取够数量上游遍历会因!yield()检查而立即终止这是将Reject置于管道中段而非末段时的核心性能优势。参考文档docs/data/it-reject.md实现it/seq.go测试it/seq_test.go示例it/seq_example_test.go组合复用it/intersect.go、it/seq.go切片对应版本slice.go、docs/data/core-reject.md【免费下载链接】lo A Lodash-style Go library based on Go 1.18 Generics (map, filter, contains, find...)项目地址: https://gitcode.com/GitHub_Trending/lo/lo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/13 15:47:49

交通标志识别工业级方案:YOLOv5s轻量化+双路径分类

简介:这是一套面向高校计算机、人工智能等专业学生的交通标志智能检测与识别毕业设计项目,基于Python实现端到端图像识别流程,覆盖数据预处理、模型训练、推理部署等核心环节,适用于课程设计、综合实验及毕设实践。资源包共298个文…

2026/9/13 15:47:49

Vue项目浏览器标签图标与标题设置全指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/13 15:47:49

PostGIS+GeoServer+OpenLayers:从空间数据库到WMS地图服务全栈实战

做WEBGIS开发这几年,最常被新人问到的一个问题就是:"空间数据到底是怎么从数据库一路渲染到浏览器里的?"很多人玩过Leaflet画点线面,也听过PostGIS和GeoServer,但真正把"创建空间数据库、发布数据、调用…

2026/9/13 15:47:49

PDFPatcher 书签编辑:从导出 XML 到批量校验页码

PDFPatcher 书签编辑:从导出 XML 到批量校验页码 【免费下载链接】PDFPatcher PDF补丁丁——PDF工具箱,可以编辑书签、剪裁旋转页面、解除限制、提取或合并文档,探查文档结构,提取图片、转成图片等等 项目地址: https://gitcode…

2026/9/13 15:47:49

10 分钟跑通 DataHub Docker Compose 部署:两条命令从零到首屏

10 分钟跑通 DataHub Docker Compose 部署:两条命令从零到首屏 【免费下载链接】datahub The Context Platform for your Data and AI Stack 项目地址: https://gitcode.com/GitHub_Trending/da/datahub DataHub 是面向数据与 AI 栈的元数据上下文平台&#…

2026/9/13 15:42:48

拒绝签名破解与风控绕过,平台数据获取的合规路径解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/12 14:32:17

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/13 11:18:28

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码