Hugo 模板函数 collections.In(in):判断切片、数组与字符串中是否包含指定值
【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo
collections.In是 Hugo 模板引擎提供的集合操作函数,用于判断某个值是否存在于给定的切片(slice)、数组(array)或字符串中,并返回布尔结果。在模板中以别名in调用,常用于条件渲染、菜单或标签过滤、内容分类判断等场景。阅读本文后,你将掌握in的两种匹配模式(成员匹配与子串匹配)、其底层实现原理,以及在实际模板中的典型用法与边界行为。
函数签名与基本信息
根据 函数文档 的 front matter 定义:
- 函数名:
collections.In - 别名:
in - 返回类型:
bool - 签名:
collections.In SLICE|STRING VALUE
即第一个参数是待检索的容器(切片/数组或字符串),第二个参数是目标值,函数返回目标值是否存在于容器中。函数在模板命名空间中的注册位于 tpl/collections/init.go,其中将"in"注册为该方法映射的别名,并附带一个子串匹配的内置示例:
{{ if in "this string contains a substring" "substring" }}Substring found!{{ end }}基础用法:判断值是否存在于切片中
官方文档给出的第一个示例是切片成员匹配:
{{ $s := slice "a" "b" "c" }} {{ in $s "b" }} → truein会对切片逐个元素进行比较。若目标值等于其中任意一个元素,返回true,否则返回false。这一模式在模板中的典型应用是"白名单"判断,例如仅对特定分类渲染内容:
{{ $allowed := slice "news" "blog" "docs" }} {{ if in $allowed .Section }} <p>该内容属于允许展示的板块</p> {{ end }}需要注意:切片匹配是严格按元素比较的,目标值必须与某个元素完全相等才会命中,不存在部分匹配或模糊匹配。
基础用法:判断子串是否存在于字符串中
官方文档给出的第二个示例是字符串子串匹配:
{{ $s := "abc" }} {{ in $s "b" }} → true当第一个参数是字符串时,in退化为"子串包含"判断,只要第二个参数是第一个字符串的一部分即返回true。这在页面路径、URL、front matter 字段等字符串内容的条件判断中非常实用:
{{ if in .RelPermalink "/tags/" }} 当前页面是标签页 {{ end }}底层实现原理
collections.In的完整实现位于 tpl/collections/collections.go,其执行逻辑分为三个阶段:
1. 空值短路
if l == nil || v == nil { return false, nil }两个参数中任意一个为nil,直接返回false,不会报错。
2. 数组/切片成员遍历
lv := reflect.ValueOf(l) vv := reflect.ValueOf(v) vvk := normalize(vv) switch lv.Kind() { case reflect.Array, reflect.Slice: for i := range lv.Len() { lvv, isNil := hreflect.Indirect(lv.Index(i)) if isNil { continue } lvvk := normalize(lvv) if lvvk == vvk { return true, nil } } }- 通过
reflect判断第一个参数的底层类型是否为Array或Slice,是则逐元素遍历; - 元素为
nil指针时跳过(continue),不参与比较; - 每个元素与目标值都经过
normalize处理后再比较,这一步骤负责将类型统一归一化,因此[]int{123}中的123与整数目标123能够正确匹配(测试用例见 tpl/collections/where_test.go)。从源码结构看,normalize的目的就是消除不同数值类型间的差异,让整型、浮点型等可以互相比较。
3. 字符串子串回退
ss, err := cast.ToStringE(l) if err != nil { return false, nil } su, err := cast.ToStringE(v) if err != nil { return false, nil } return strings.Contains(ss, su), nil若第一个参数不是数组/切片(例如是字符串),则将其与第二个参数都强制转换为字符串,然后调用 Go 标准库的strings.Contains判断子串关系。这里值得注意的一个推断:转换失败或字符串类型不匹配时,函数返回false,nil而非报错——这也意味着当第一个参数既不是集合也不是可转字符串的值时,结果恒为false。
边界行为与注意事项
结合源码与测试可以总结出以下边界行为:
| 场景 | 结果 | 依据 |
|---|---|---|
| 目标值存在于切片中 | true | collections.go |
| 目标值不存在于切片中 | false | 同上 |
| 空切片中查找任意值 | false | 测试用例{reflect.ValueOf(123), reflect.ValueOf([]int{}), "in", expect{false, false}},见 where_test.go |
| 目标值为子串 | true(子串匹配) | 测试用例{reflect.ValueOf("foo"), reflect.ValueOf("bar-foo-baz"), "in", expect{true, false}},见 where_test.go |
任意参数为nil | false | collections.go |
目标值是time.Time且存在于时间切片中 | true | 测试用例见 where_test.go |
需要特别强调两点:
- 子串匹配是大小写敏感的:
in "abc" "B"返回false,如需忽略大小写应结合lower/upper函数预处理后再判断。 - 字符串子串匹配与切片成员匹配是两种截然不同的语义:前者做"部分包含",后者做"完全相等"。当目标值恰好是空字符串时,
strings.Contains(s, "")恒为true,这是 Go 标准库的行为,使用时需留意。
与 where 操作符in的区别
Hugo 的where函数也支持in/not in操作符(见 tpl/collections/where.go 与 where_test.go),其语义是"某字段的值是否包含在给定集合中",用于对页面集合做筛选。而collections.In是独立的条件判断函数,直接返回布尔值。二者适用场景不同:
{{/* where 操作符:筛选出 params.tags 字段包含 "hugo" 的页面 */}} {{ $pages := where .Site.RegularPages "Params.tags" "in" (slice "hugo") }} {{/* collections.In 函数:对单个值做包含判断 */}} {{ if in .Params.tags "hugo" }} 该页面打上了 hugo 标签 {{ end }}实战组合示例
结合range与if,in可以写出可读性很高的条件逻辑。例如在分类页面中高亮当前激活的分类:
{{ $current := .Title }} <ul> {{ range site.Menus.main }} <li class="{{ if in $current .Name }}active{{ end }}"> <a href="{{ .URL }}">{{ .Name }}</a> </li> {{ end }} </ul>再如基于多个关键词的简单内容标记:
{{ $keywords := slice "tutorial" "guide" "reference" }} {{ if in $keywords .Params.category }} <span class="badge">精选内容</span> {{ end }}总结
collections.In(别名in)是 Hugo 模板中高频使用的小而精的函数:对切片/数组执行严格的成员匹配,对字符串执行子串包含匹配。理解其"数组走遍历比较、字符串走strings.Contains"的双路径实现,以及空值短路、类型归一化、大小写敏感等边界行为,能帮助你在模板开发中写出更准确、更健壮的条件判断逻辑。
【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考