yq 的 min 操作符:求取标量序列最小值实战指南
【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq
导读
本文围绕 yq(portable command-line YAML/JSON/XML/CSV/TOML/HCL/properties 处理器)中的min操作符展开,讲解它如何在传入的标量值序列中计算最小值。读完本文,你将掌握min对整数、字符串的求值行为、对空序列的处理结果,以及它底层与max、compare操作符共享的比较实现原理,并能在真实管道中组合使用该操作符。
min 操作符是什么
min是 yq 表达式语言中一个零参数(NumArgs: 0)的操作符,其功能是:计算传入的标量值序列中的最小值。该定义来自官方操作符文档 min.md,是min唯一的核心语义。
在 yq 中,一个表达式的结果往往是一个由多个候选节点(CandidateNode)组成的序列。min会遍历这个序列,并从中挑出在类型比较规则下“最小”的那一个作为结果返回。与之成对的是max操作符,功能为求最大值,二者实现几乎完全对称。
求整数序列的最小值
输入示例
准备一个sample.yml文件,内容为一个整数数组:
- 99 - 16 - 12 - 6 - 66执行命令
yq 'min' sample.yml输出结果
6可以看到,min对整数序列按数值大小比较,返回了序列中最小的元素6,而其他元素被丢弃。在 yq 的测试套件中,该场景被描述为 "Minimum int",输入[99, 16, 12, 6, 66],期望输出为路径P[3]、类型!!int、值6的节点(见 operators_compare_test.go)。
求字符串序列的最小值
输入示例
- foo - bar - baz执行命令
yq 'min' sample.yml输出结果
bar字符串序列的最小值是按字典序(逐个字符的字节大小)比较得出的:bar<baz<foo,因此返回bar。对应测试场景 "Minimum string" 的期望输出为路径P[1]、类型!!str、值bar(见 operators_compare_test.go)。
空序列上的 min:无输出
输入示例
[]执行命令
yq 'min' sample.yml输出结果
当输入为空序列(没有任何标量元素)时,min不产生任何输出。对应测试场景 "Minimum of empty" 的期望结果为空列表(见 operators_compare_test.go)。这一点在编写脚本时要特别注意:对空数据求 min 不会报错,但也不会得到任何值,下游管道需要自行处理“无结果”的情况。
源码实现:min 与 max 共用一套比较内核
min在源码中位于 operator_compare.go:
func minOperator(d *dataTreeNavigator, context Context, _ *ExpressionNode) (Context, error) { log.Debug(("Min")) return superlativeByComparison(d, context, compareTypePref{Greater: false}) } func maxOperator(d *dataTreeNavigator, context Context, _ *ExpressionNode) (Context, error) { log.Debug(("Max")) return superlativeByComparison(d, context, compareTypePref{Greater: true}) }两者的差别仅仅在于传入的compareTypePref.Greater布尔值:min为false(取更小者),max为true(取更大者)。
核心逻辑superlativeByComparison(operator_compare.go)执行以下步骤:
- 对当前上下文中每个匹配节点调用
splat展开(见 operator_traverse_path.go),把容器节点铺平为元素序列; - 以序列第一个元素为临时“最值”,依次与后续每个元素调用
compare(prefs)比较函数; - 若当前元素比已记录的“最值”更符合条件(对 min 而言是更小),则替换记录;
- 最终把每个序列求得的最值节点收集进结果列表返回。
支持的类型与比较规则
compare(prefs)内部的compareScalars(operator_compare.go)按节点标签(Tag)分派比较:
!!intvs!!int:解析为 int64 后按数值比较;!!int/!!float混合:解析为 float64 后按数值比较,因此整数与浮点数可以直接混排求 min;!!strvs!!str:按字符串值直接比较(字典序);!!timestamp(或可解析为时间戳的字符串):走compareDateTime(operator_compare.go),按时间先后比较,时间布局取自当前上下文(context.GetDateTimeLayout());- 映射(MappingNode)与数组(SequenceNode):不支持参与比较,会返回 "maps not yet supported for comparison" / "arrays not yet supported for comparison" 错误;
!!null:与任何非空值比较均返回false(两者都为 null 时仅在OrEqual语义下返回真)。
这意味着min并不仅限于文档示例中的整数与字符串,日期时间序列同样可以直接求最小值,但映射、数组等复合结构会直接报错,使用时需先对嵌套结构进行展开或选取。
操作符注册:语法层面如何绑定 min
在词法与解析层面,min被注册为独立操作类型,见 operation.go:
var minOpType = &operationType{Type: "MIN", NumArgs: 0, Precedence: 40, Handler: minOperator} var maxOpType = &operationType{Type: "MAX", NumArgs: 0, Precedence: 40, Handler: maxOperator}其中NumArgs: 0说明min不接收参数(对应文档中直接以yq 'min'调用的用法),Precedence: 40决定了它与其他操作符组合时的结合优先级,Handler指向上述minOperator实现。因此你可以在管道中把它与其他表达式组合,例如先筛选再求最值:
# 从对象数组中取出 scores 字段,再求最小值 yq '[.items[].score] | min' data.yaml小结
min是 yq 中最常用的聚合操作符之一:对整数、浮点数、字符串、时间戳等标量序列按各自的类型规则取最小值,对空序列返回空结果,对复合结构(映射/数组)则报错。其实现与max共用superlativeByComparison比较内核,仅由一个布尔标志区分大小方向,理解这一点后,你就能把min/max灵活嵌入到自己的 YAML 处理管道中。
【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考