Hugo 模板函数 collections.In(in):判断切片、数组与字符串中是否包含指定值
2026/9/18 23:02:50 网站建设 项目流程

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" }} → true

in会对切片逐个元素进行比较。若目标值等于其中任意一个元素,返回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判断第一个参数的底层类型是否为ArraySlice,是则逐元素遍历;
  • 元素为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

边界行为与注意事项

结合源码与测试可以总结出以下边界行为:

场景结果依据
目标值存在于切片中truecollections.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
任意参数为nilfalsecollections.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 }}

实战组合示例

结合rangeifin可以写出可读性很高的条件逻辑。例如在分类页面中高亮当前激活的分类:

{{ $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),仅供参考

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

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

立即咨询