yq 的 map 与 map_values 操作符:数组与对象批量变换实战指南
2026/9/14 8:00:00 网站建设 项目流程

yq 的 map 与 map_values 操作符:数组与对象批量变换实战指南

【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq

mapmap_values是 yq(portable command-line YAML/JSON/XML/CSV/TOML/HCL/properties processor)中用于批量变换集合元素的一对核心操作符:map作用于数组的每个元素,map_values作用于对象的每个值。本文以 pkg/yqlib/doc/operators/map.md 为骨架,结合 yq 源码(pkg/yqlib/operator_map.gopkg/yqlib/operation.gopkg/yqlib/operator_map_test.go等)深入讲解二者的语法、用法、底层实现与测试验证,读完即可在 YAML/JSON 处理脚本中熟练运用批量映射能力。

基本语义

在 yq 中:

  • map(expr):对数组中的每一个元素执行表达式expr,返回一个新数组,元素数量与输入保持一致;
  • map_values(expr):对对象(map)中的每一个执行表达式expr,键保持不变,仅替换值。

二者的共同点是:映射表达式内部通过.引用"当前正在处理的元素/值",这与 jq 的map语义一致,便于从 jq 迁移的用户快速上手。

map:数组元素映射

官方示例

假设存在sample.yml文件,内容为:

- 1 - 2 - 3

执行:

yq 'map(. + 1)' sample.yml

输出:

- 2 - 3 - 4

map(. + 1)的含义是:把数组中的每个元素依次取出来,代入.,计算. + 1,再将所有计算结果组装成一个新数组。

映射空数组

当输入数组为空时,map不会报错,也不会产生任何元素。源码pkg/yqlib/operator_map.go中明确处理了这一分支:当splatted.MatchingNodes.Len() == 0时,直接复制原始候选节点(candidate.Copy())并放入结果集。对应测试(pkg/yqlib/operator_map_test.go中 "mapping against an empty array should do nothing")验证:

yq '[] | map(. + 42)' sample.yml

输出为空数组[],不会抛异常。

映射结果再展开

map的输出本身是一个数组,可以在其后追加[]将其展开为流式节点。例如测试用例:

yq 'map(. + 1)[]' sample.yml

输入[1, 2]时输出两个独立节点:

2 3

这在后续需要逐元素继续管道处理时非常实用。

对嵌套数组映射

map可以结合路径选择作用于嵌套结构。测试用例中:

yq '.[] |= map(3 + .)' sample.yml

输入[[], [5]]时输出[[], [8]]:外层.[]遍历每一个子数组,map(3 + .)对每个子数组内部元素做变换,空子数组保持不变。

map_values:对象值映射

官方示例

假设sample.yml内容为:

a: 1 b: 2 c: 3

执行:

yq 'map_values(. + 1)' sample.yml

输出:

a: 2 b: 3 c: 4

abc全部保留,值1/2/3分别被替换为2/3/4

对空对象映射

与空数组类似,对空对象{}执行map_values同样安全无副作用,测试用例验证yq 'map_values(3 + .)'作用于{}时输出仍为{},不会产生错误。

结果展开为节点流

测试用例 "map values splat" 展示了map_values(. + 1)[]的用法:输入{a: 1, b: 2}时,输出为两条独立记录(每条带各自路径):

a: 2 b: 3

map_values配合[]展开后,每个键值对成为独立节点,便于继续使用.keyto_entries等操作符做二次处理。

源码级实现解析

词法识别:map 与 map_values 的标记

在词法层,pkg/yqlib/lexer_participle.go中定义了两个 token:

  • simpleOp("map", mapOpType):将关键字map解析为mapOpType
  • {"MapValues", 'map_?values', opToken(mapValuesOpType), 0}:使用正则map_?values匹配map_valuesmapvalues两种写法(即下划线可选),解析为mapValuesOpType

操作符注册:参数个数与优先级

pkg/yqlib/operation.go中注册了这两个操作符:

var mapOpType = &operationType{Type: "MAP", NumArgs: 1, Precedence: 52, Handler: mapOperator, CheckForPostTraverse: true} var mapValuesOpType = &operationType{Type: "MAP_VALUES", NumArgs: 1, Precedence: 52, Handler: mapValuesOperator, CheckForPostTraverse: true}

二者均为单参数操作符(NumArgs: 1,即括号内的映射表达式),优先级为 52(与filterpickomit同级),并开启CheckForPostTraverse——这就是map(...)[]之后能直接展开遍历的原因。

mapOperator:求值后收集

核心实现位于pkg/yqlib/operator_map.gomapOperator函数,流程为:

  1. 遍历所有匹配节点(context.MatchingNodes);
  2. 对每个候选节点调用splat(见pkg/yqlib/operator_traverse_path.go中的splat函数)将其展开为元素序列;
  3. 若展开结果为空,直接复制原节点(保证空数组/空对象安全);
  4. 否则对展开结果执行表达式树的 RHS(即括号内的映射表达式);
  5. collectTogetherpkg/yqlib/operator_collect.go)把所有结果收集进一个新的!!seq节点,并继承原候选节点的样式collected.Style = candidate.Style),从而保持输出风格一致。

mapValuesOperator:原地更新

mapValuesOperator的实现则走了另一条路径:对每个候选节点 splat 之后,构造一个assignUpdateExpOperationType: assignOpType, UpdateAssign: true),把 RHS 表达式作为更新目标,调用assignUpdateOperator原地更新各节点的值。这正是map_values能"保留键、只改值"的底层原因——它本质上是一次批量赋值更新,而非重新组装集合。

相关操作符与组合用法

map 与.[] |的区别

map(expr)可以视为对"当前集合内每个元素应用表达式"的语法糖,等价于先将数组展开、逐个变换再重新收集。它与.[] |= expr的差异在于:map显式返回一个新数组,且对空数组有专门的兜底处理;而.[] |=是路径赋值语义。测试用例中.[] |= map(3 + .)也展示了二者可以嵌套配合。

map_values 与 entries 系列操作符

map_values只关注值;若需要同时变换键和值,可与to_entries/from_entriespkg/yqlib/operator_entries.go)配合:先用to_entries把对象转成{key, value}数组,用map自由变换后再from_entries还原为对象。这也是社区常用的"键值同时处理"模式:

yq 'to_entries | map({key: .key, value: (.value * 2)}) | from_entries' sample.yml

组合管道示例

映射表达式可以是任意 yq 表达式,包括函数调用、条件判断与嵌套映射:

# 对数组中的数字取绝对值 yq 'map(. * -1)' sample.yml # 根据条件保留性变换对象值 yq 'map_values(select(. > 1) | . + 100)' sample.yml # 数组内嵌套对象字段批量修改 yq 'map(.price = .price * 0.9)' sample.yml

测试验证

pkg/yqlib/operator_map_test.go通过mapOperatorScenarios表驱动测试覆盖了本操作符的核心行为,其中包括:

  • Map arraymap(. + 1)[1,2,3]输出[2, 3, 4]
  • Map object valuesmap_values(. + 1){a: 1, b: 2, c: 3}输出{a: 2, b: 3, c: 4}
  • 空数组、空对象场景的幂等安全行为;
  • 多文档(document+document2)下每个文档独立映射;
  • map(...)[]map_values(...)[]的展开输出。

这些场景与文档示例一一对应,可作为理解map/map_values行为边界的可靠参考。

总结

mapmap_values是 yq 中处理集合数据的高频操作符:前者面向数组逐元素映射,后者面向对象逐值映射,二者语法一致(括号内用.引用当前元素),且对空集合安全、支持管道展开。通过阅读 pkg/yqlib/operator_map.go 可以进一步理解其"求值-收集"与"批量赋值更新"两条实现路径,结合 pkg/yqlib/operator_map_test.go 的测试场景即可在真实脚本中放心使用。

【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询