news 2026/9/13 7:38:20

lo 库 it.FilterValues 详解:Go 1.23 迭代器风格的 map 值过滤函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
lo 库 it.FilterValues 详解:Go 1.23 迭代器风格的 map 值过滤函数

lo 库 it.FilterValues 详解:Go 1.23 迭代器风格的 map 值过滤函数

【免费下载链接】lo💥 A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...)项目地址: https://gitcode.com/GitHub_Trending/lo/lo

本文聚焦 lo 库 it 包中的 FilterValues 函数,讲解其函数签名、谓词用法与全部示例场景,并结合 it/map.go 的源码实现、it/map_test.go 的测试用例,说明它作为iter.Seq惰性序列的工作原理,以及与 core 包中同步切片版本 lo.FilterValues 的差异,帮助读者在 Go 1.23+ 的迭代器场景下正确选择和使用这一工具函数。

一、函数定位与核心语义

it.FilterValues是 lo 库面向 Go 1.23 引入的iter标准库设计的序列化工具之一。其官方文档描述为:

Filters map values based on a predicate function that takes both key and value. Returns a slice of values that satisfy the predicate.

(根据一个同时接收键和值的谓词函数过滤 map 的值,返回满足谓词的值切片。)

函数签名如下,定义在 it/map.go:

func FilterValuesK comparable, V any bool) iter.Seq[V]

关键参数说明:

参数约束/类型说明
Kcomparablemap 键类型,要求可比较
Vanymap 值类型,任意类型
inmap[K]V待过滤的输入 map
predicatefunc(key K, value V) bool谓词函数,同时接收键与值,返回true时该值被保留
返回值iter.Seq[V]惰性序列,可被range遍历或slices.Collect收集为切片

从源码注释看,FilterValues被明确定位为 “a mix of Filter and Values”——即Filter(按条件筛选)与Values(提取 map 值)两个能力的组合:遍历时对每个键值对执行谓词判断,只有判断为真时才把值 yield 出去。

二、完整示例场景

以下示例完整继承自官方文档 docs/data/it-filtervalues.md,覆盖数值过滤、字符串长度过滤、键值联合判断、空 map 与浮点数过滤五类典型场景。由于it包基于 Go 1.23 的iter.Seq实现,实际使用时通常先用slices.Collect把序列收集为切片,再断言结果:

package main import ( "fmt" "iter" "slices" "strings" "github.com/samber/lo/it" ) func collectV any []V { return slices.Collect(seq) } func main() { // 场景 1:数值过滤,取出值大于 2 的元素 m := map[string]int{ "apple": 3, "banana": 5, "cherry": 2, "date": 0, } result := collect(it.FilterValues(m, func(key string, value int) bool { return value > 2 })) // 值为 3 和 5(对应 "apple" 和 "banana"),元素顺序不保证 // 场景 2:字符串长度过滤,取出长度为 3 的值 numberMap := map[int]string{1: "one", 2: "two", 3: "three", 4: "four"} result = collect(it.FilterValues(numberMap, func(key int, value string) bool { return len(value) == 3 })) // "one"、"two"、"three" // 场景 3:键与值联合判断,键以 "a" 开头且年龄 >= 20 personMap := map[string]int{"alice": 25, "bob": 30, "charlie": 17} result = collect(it.FilterValues(personMap, func(key string, age int) bool { return strings.HasPrefix(key, "a") && age >= 20 })) // 仅 "alice" 的值 25 // 场景 4:空 map,谓词恒为 true 也不产生任何元素 emptyMap := map[string]int{} result = collect(it.FilterValues(emptyMap, func(key string, value int) bool { return true })) // 空切片 // 场景 5:浮点数过滤,只保留正值 dataMap := map[string]float64{"a": 1.5, "b": -2.0, "c": 0.0, "d": 3.14} result = collect(it.FilterValues(dataMap, func(key string, value float64) bool { return value > 0 })) // 1.5 和 3.14 fmt.Println(result) }

需要特别注意的是:文档示例注释中把结果写作[]int{3, 5}这类形式,但由于 Go map 本身的遍历顺序是随机的,FilterValues输出序列中元素的相对顺序并不保证与 map 键的某种排列一致。因此对结果做精确切片比较时,建议先排序或使用ElementsMatch之类的无序断言。

三、源码级实现剖析

it.FilterValues的完整实现见 it/map.go:

// FilterValues transforms a map into a sequence based on predicate returns true for specific elements. // It is a mix of Filter and Values. func FilterValuesK comparable, V any bool) iter.Seq[V] { return func(yield func(V) bool) { for k, v := range in { if predicate(k, v) && !yield(v) { return } } } }

从源码结构看,其实现包含三个值得关注的细节:

  1. 返回的是闭包而非切片。函数体并不立即遍历 map,而是返回一个捕获了inpredicate的函数值。只有当调用方真正range这个序列时,map 遍历才会发生,这是一种惰性求值(lazy evaluation)模式。
  2. yield返回值的检查实现了提前终止yield(v)返回false时闭包立刻return,这意味着消费方在迭代中途breakreturn时,谓词不会在剩余键值对上被白白执行。这一点在测试基础设施 it/lo_test.go 的assertSeqSupportBreak辅助函数中有系统性验证:
// assertSeqSupportBreak checks whether it is possible to break iteration over a [iter.Seq]. func assertSeqSupportBreakT any iter.Seq[T] { t.Helper() assert.NotPanics(t, func() { for range seq { break } for range seq { return } }) return seq }
  1. 该实现受编译条件约束。it/map.go 文件首行带有//go:build go1.23构建标签,因此it包的所有函数(包括FilterValues)只在 Go 1.23 及以上工具链下参与编译。项目整体 go.mod 声明的最低版本是 Go 1.18,即 core 包兼容 Go 1.18+,而it包是面向 Go 1.23 迭代器标准库的增量扩展。

四、与 core 包 lo.FilterValues 的对照

同一个仓库中,core 包提供了同步切片版本的 lo.FilterValues,其签名与文档中的 core 版一致:

// FilterValues transforms a map into a slice based on predicate returns true for specific elements. // It is a mix of lo.Filter() and lo.Values(). func FilterValuesK comparable, V any bool) []V { result := make([]V, 0, len(in)) for k, v := range in { if predicate(k, v) { result = append(result, v) } } return result }

两个版本的对应关系可以归纳为:

维度lo.FilterValues(core,map.go)it.FilterValues(it/map.go)
返回值[]V,立即物化为切片iter.Seq[V],惰性序列
执行时机调用即遍历整个 map被 range/Collect 时才遍历
提前终止不支持(谓词必然跑完所有条目)支持(yield 返回 false 即停止)
运行环境Go 1.18+(泛型即可)Go 1.23+(依赖标准库iter包)
典型搭配直接使用slices.Collect收集,或与其他iter.Seq组合

如果下游逻辑需要错误处理能力,core 包还有带错误返回的 FilterValuesErr:谓词签名变为func(key K, value V) (bool, error),谓词一旦返回非空 error,迭代立即停止并返回该错误。而it包的 map.go 目前提供的是无错误变体的FilterValues;从源码结构看,若需要“错误 + 迭代器”的组合,可以先用 core 的FilterValuesErr收集为切片,再按需转换。

五、相关函数与组合使用

在 it/map.go 中,FilterValues与一族 map 序列化工具紧邻定义,可自然组合:

  • FilterKeys:与FilterValues对称,返回满足谓词的序列,即 “Filter + Keys” 的组合;
  • Values:不带谓词,返回全部值的序列。FilterValues可以理解为Values加上了按 key/value 判断的闸门;
  • Keys、Entries:分别返回键序列与键值对序列,是 map 到序列转换的基础工具;
  • FilterMapToSeq:谓词升级为转换函数func(key K, value V) (R, bool),在过滤的同时做类型变换,功能上是FilterValues的泛化。

一个典型的组合用法:先用FilterValues筛出值序列,再交给it包的其他序列函数做二次处理,例如排序后取前 N 项,整个过程无需中间切片分配。

六、测试用例验证

it.FilterValues的行为在 it/map_test.go 中有专门的单元测试TestFilterValues,包含两个子场景:

func TestFilterValues(t *testing.T) { t.Parallel() t.Run("string values filtered by matching predicate", func(t *testing.T) { t.Parallel() is := assert.New(t) result1 := FilterValues(map[int]string{1: "foo", 2: "bar", 3: "baz"}, func(k int, v string) bool { return v == "foo" }) assertSeqSupportBreak(t, result1) is.Equal([]string{"foo"}, slices.Collect(result1)) }) t.Run("int values with predicate always false", func(t *testing.T) { t.Parallel() is := assert.New(t) result2 := FilterValues(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(k string, v int) bool { return false }) is.Empty(slices.Collect(result2)) }) }

第一个子用例验证正常过滤路径(只保留值为"foo"的条目),并在收集结果前先执行assertSeqSupportBreak,确认中途break/return不会导致 panic;第二个子用例验证谓词恒为false时序列为空。此外,it/map_example_test.go 提供了可直接运行的示例测试:

func ExampleFilterValues() { kv := map[int]string{1: "foo", 2: "bar", 3: "baz"} result := slices.Collect(FilterValues(kv, func(k int, v string) bool { return v == "foo" })) fmt.Printf("%v", result) // Output: [foo] }

运行go test ./it/即可复现上述验证(需 Go 1.23 工具链,以满足it/map.go的构建标签)。

七、使用要点小结

  1. 版本前提it.FilterValues依赖 Go 1.23 的标准库iter包,文件头部的构建标签见 it/map.go;低版本环境应改用 core 包 map.go 中的同步切片版本。
  2. 惰性语义:返回的iter.Seq[V]可被多次range,每次都会重新遍历底层 map 并重新执行谓词;若需要固化结果,用slices.Collect一次性收集。
  3. 顺序不确定:结果顺序继承自 map 的随机遍历顺序,做断言或对外输出时不要假设固定排列。
  4. 谓词同时拿到 key 和 value:这是FilterValues相对“先Valuesfilter”两步写法的核心优势——单步内即可完成键值联合判断,且只遍历 map 一次。
  5. 组合方向:与 FilterKeys、Entries、FilterMapToSeq 等函数共同构成 it 包的 map 序列化工具体系;需要错误处理时参考 core 包 FilterValuesErr 的设计。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 7:37:45

如何用 AI SDK 的 Batch API 提交异步批处理并跟踪结果状态

如何用 AI SDK 的 Batch API 提交异步批处理并跟踪结果状态 【免费下载链接】ai The AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents 项目地址: https://gitcode.co…

作者头像 李华
网站建设 2026/9/13 7:36:24

半导体探针台国产化突破与技术解析

1. 探针台的基础概念与国产化意义 探针台(Probe Station)是半导体测试领域的关键设备,主要用于晶圆级芯片的电性能测试。它通过精密机械结构和探针卡(Probe Card)的配合,实现对微米级电极的精准接触测量。国…

作者头像 李华
网站建设 2026/9/13 7:34:15

蓝牙音箱选购指南:从礼物逻辑到场景化推荐,送朋友不踩坑

/* 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 7:28:34

清华大学开源端侧Agent智能体:动态计算图与混合精度推理解析

1. 项目背景与核心价值清华大学开源的端侧Agent智能体项目,标志着AI技术从云端向边缘设备迁移的重要里程碑。这个GitHub项目之所以引发广泛关注,关键在于它解决了传统云端Agent的三大痛点:延迟依赖、隐私泄露风险和离线场景限制。我在实际部署…

作者头像 李华
网站建设 2026/9/13 7:27:26

Delta并联机器人MATLAB运动学仿真实践

1. Delta并联机器人运动学仿真概述Delta并联机器人作为典型的空间三自由度并联机构,凭借其高速、高精度的特点,在分拣、包装等工业场景中广泛应用。这次我们使用MATLAB搭建完整的运动学仿真环境,重点解决两个核心问题:如何根据末端…

作者头像 李华