ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

使用 lo.FilterReject 一次遍历同时完成保留与剔除:Go 泛型切片双路过滤实战

2026/9/13 1:25:15 拓冰建站 浏览量
使用 lo.FilterReject 一次遍历同时完成保留与剔除:Go 泛型切片双路过滤实战 使用 lo.FilterReject 一次遍历同时完成保留与剔除Go 泛型切片双路过滤实战【免费下载链接】lo A Lodash-style Go library based on Go 1.18 Generics (map, filter, contains, find...)项目地址: https://gitcode.com/GitHub_Trending/lo/lolo.FilterReject是 lo一个基于 Go 1.18 泛型的 Lodash 风格库提供的切片过滤工具它接收一个谓词函数一次遍历同时返回“满足谓词”与“不满足谓词”两个切片让原本需要FilterReject两次遍历的过滤逻辑合并为一次。读完本文你将掌握FilterReject的签名语义、底层实现、与Filter/Reject/PartitionBy的差异对比以及它在实际项目中的典型用法。函数签名与核心语义FilterReject定义于 slice.go其签名如下func FilterReject[T any, Slice ~[]T](collection Slice, predicate func(T, int) bool) (kept, rejected Slice)参数与返回值含义项类型说明collectionSlice底层类型为[]T输入切片不会被修改predicatefunc(T, int) bool谓词函数参数依次为元素值与下标index返回boolkeptSlice谓词返回true的元素按原顺序保留rejectedSlice谓词返回false的元素按原顺序保留核心语义即文档 core-filterreject.md 所述返回两个切片——谓词为真的元素kept与谓词为假的元素rejected。kept, rejected : lo.FilterReject( []int{1, 2, 3, 4}, func(x int, _ int) bool { return x%2 0 }, ) // kept: []int{2, 4} // rejected: []int{1, 3}泛型约束说明签名中的Slice ~[]T表示该函数接受任意底层类型为[]T的命名切片类型而不仅仅是内置的[]T。~波浪号类型近似约束来自 Go 1.18 的类型集合语法它让自定义切片类型也能直接调用且返回的两个切片会保持调用者的自定义类型见下文测试证据。T any意味着元素类型可以是任意类型无需满足comparable等额外约束。源码实现剖析从 slice.go 的实现可以看到一个典型的双缓冲遍历func FilterReject[T any, Slice ~[]T](collection Slice, predicate func(T, int) bool) (kept, rejected Slice) { kept make(Slice, 0, len(collection)) rejected make(Slice, 0, len(collection)) for i : range collection { if predicate(collection[i], i) { kept append(kept, collection[i]) } else { rejected append(rejected, collection[i]) } } return kept, rejected }实现要点一次遍历完成双路输出循环体内部对每个元素恰好调用一次predicate依据返回值决定落入kept还是rejected不存在二次遍历。预先分配容量两个结果切片均以len(collection)为容量预分配避免append过程中的重复扩容对大规模切片更友好。下标透传predicate的第二个参数i直接透传元素下标与 Filter、Reject 的行为保持一致便于实现依赖位置的谓词如取前 N 个、按奇偶位置过滤。纯函数、无副作用函数不会修改输入collection只读遍历符合 lo 库切片工具的一贯约定。与 Filter、Reject、PartitionBy 的关系FilterReject位于过滤工具族谱的交叉位置理解它与其他兄弟函数的关系有助于正确选型函数返回遍历次数适用场景Filter仅谓词为true的元素1 次只想保留符合条件的那一半Reject仅谓词为false的元素Filter的反操作1 次只想剔除符合条件的那一半FilterReject同时返回true/false两批元素1 次需要同时拿到“符合”与“不符合”两批数据PartitionBy见 core-partitionby.md按键值分组的任意多个切片1 次需要按多个分类键分组而非二分类值得强调的等价关系如果只调用FilterReject的第一个返回值等价于Filter只使用第二个返回值等价于Reject。但使用FilterReject能在一次遍历内同时得到两个结果避免了分别调用Filter与Reject时的两次全量遍历在谓词计算代价较高例如涉及 I/O、字符串解析或复杂计算时收益明显。从代码结构看Filter 只维护result一个缓冲Reject 同样只维护一个缓冲并取!predicate而FilterReject将二者合并进同一个循环这正是它作为“混合体”的本质。测试与类型保持验证slice_test.go 中的TestFilterReject覆盖了三条关键路径int 切片FilterReject([]int{1, 2, 3, 4}, x%2 0)断言kept []int{2, 4}、rejected []int{1, 3}与文档示例一致。string 切片FilterReject([]string{Smith, foo, Domin, bar, Olivia}, len(x) 3)断言kept []string{Smith, Domin, Olivia}、rejected []string{foo, bar}展示了基于字符串属性长度的过滤。命名切片类型保持使用自定义类型type myStrings []string调用后通过is.IsType(a, allStrings)断言返回的两个切片仍然是myStrings类型——这正是Slice ~[]T泛型约束的直接验证结果切片保留调用者的命名类型而非退化为[]string。实际应用场景示例1. 数据清洗分离合法与非法记录type Order struct { ID string Amount float64 } orders : []Order{ {ID: o-1, Amount: 120.5}, {ID: o-2, Amount: 0}, {ID: o-3, Amount: 89.0}, } valid, invalid : lo.FilterReject(orders, func(o Order, _ int) bool { return o.Amount 0 }) // valid: []Order{{o-1, 120.5}, {o-3, 89.0}} // invalid: []Order{{o-2, 0}}2. 利用下标参数的按位置过滤// 同时得到下标为偶数与奇数的元素 evenIndexed, oddIndexed : lo.FilterReject([]string{a, b, c, d}, func(_ string, i int) bool { return i%2 0 }) // evenIndexed: []string{a, c} // oddIndexed: []string{b, d}3. 与自定义命名切片类型协同type IDs []int ids : IDs{10, 21, 30, 43} big, small : lo.FilterReject(ids, func(x int, _ int) bool { return x 20 }) // big: IDs{21, 30, 43} // small: IDs{10} // big 与 small 的类型均为 IDs可直接继续传给其他接收 IDs 的函数性能基准仓库在 benchmark/core_slice_bench_test.go 中提供了BenchmarkFilterReject对不同长度的 int 切片lengths定义的规模循环执行lo.FilterReject(ints, v%2 0)以测量吞吐。这印证了该函数在热路径上被频繁调用的场景结合其实现中预分配容量 单次遍历的特点可预期在大规模切片与昂贵谓词场景下比“Filter一次 Reject一次”的两次遍历方案更优。仓库未公开具体的基准数值如需量化对比可在本地运行go test -benchBenchmarkFilterReject ./benchmark/实测。使用建议与注意事项谓词需为纯函数predicate会在一次遍历内被每个元素调用一次若谓词带副作用或依赖外部可变状态双路输出的正确性将难以保证。空输入安全传入空切片时两个结果均为空切片而非nil因使用make创建与Filter/Reject行为一致便于直接使用len()判断。二分类场景优先选用需要“符合/不符合”两批数据时直接使用FilterReject而非两次调用Filter/Reject若需要按多个键分多组则应改用PartitionBy。保持类型一致性由于Slice ~[]T约束自定义切片类型调用后返回类型不变可放心与强类型 API 衔接。FilterReject在 lo 的整个切片工具集中扮演着“一次遍历、双向输出”的独特角色是数据清洗、分流处理与双路统计场景下简洁而高效的选择。【免费下载链接】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),仅供参考