该内置函数将一个序列拆分为多个序列,这些序列的大小由内置函数的第一个参数指定(如
mySeq?chunk(3)
)。结果是这些序列的序列。除非指定了第二个参数(例如
mySeq?chunk(3, '-')
),否则最后一个序列可能比给定的大小短,这是用于将最后一个序列的大小组成给定大小的项。例:
<#assign seq = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']>
<#list seq?chunk(4) as row>
<#list row as cell>${cell} </#list>
</#list>
<#list seq?chunk(4, '-') as row>
<#list row as cell>${cell} </#list>
</#list>
输出将是:
a b c d
e f g h
a b c d
e f g h
i j - -
该内置程序主要用于以表格/列格式输出序列。与 HTML 表一起使用时,第二个参数通常为"\xA0"
(即不间断空格字符的代码,也称为“ nbsp”),因此不会丢失空 TD-s 的边框。
第一个参数必须是一个至少为 1 的数字。如果该数字不是整数,它将无提示地向下舍入为整数(即 3.1 和 3.9 都将舍入为 3)。第二个参数可以是任何类型和值。
返回一个新序列,该序列包含 Importing 序列中的元素,从第一个与参数谓词(条件)不匹配的元素开始。之后,包括所有元素,无论它们是否与谓词匹配。有关参数的更多详细信息和其他详细信息,请参见filter built-in,但请注意filter
中的条件具有相反的含义(要保留的内容,而不是要删除的内容)。
示例和与filter
的比较:
<#assign xs = [1, 2, -3, 4, -5, 6]>
Drop while positive:
<#list xs?drop_while(x -> x > 0) as x>${x} </#list>
Filer for positives:
<#list xs?filter(x -> x > 0) as x>${x} </#list>
Drop while positive:
-3 4 -5 6
Filer for positives:
1 2 4 6
如您所见,一旦drop_while
遇到与谓词(x > 0
)不匹配的第一个元素,它将停止删除这些元素。另一方面,filter
保留与相同谓词匹配的元素,并且不会停止。
另请参阅:take_while built-in
Note:
自 2.3.29 起提供此内置功能
返回一个新序列,该序列仅包含参数条件(谓词)为其返回true
的元素。例如:
<#assign xs = [1, -2, 3, 4, -5]>
Positives:
<#list xs?filter(x -> x > 0) as x>${x} </#list>
Negatives:
<#list xs?filter(x -> x < 0) as x>${x} </#list>
Positives:
1 3 4
Negatives:
-2 -5
此内置有一个必填参数,谓词(过滤条件,要保留的内容)。可以通过 3 种方式指定谓词:
作为单个参数lambda expression:element -> predicate
。这样,element
是变量名,您可以使用它来引用predicate
中的当前元素,而predicate
是任意复杂的expression,必须返回布尔值(true
或false
)。上面显示了一个示例。再次注意谓词可以是任意复杂的,例如products?filter(product -> product.discounted && !user.hasBought(product))
中的谓词。
作为具有单个参数的function或方法,并返回布尔值。例如,上面的“否定”示例可以这样实现:
<#function negative(x)>
<#return x < 0>
</#function>
Negatives:
<#list xs?filter(negative) as x>${x} </#list>
请注意,我们是如何仅通过名称引用该函数,而没有调用它。同样,如果数据模型中有一个名为utils
的 Java 对象,并且它具有boolean isNegative(Number n)
方法,则可以像xs?filter(utils.isNegative)
那样使用它。
Note:
请记住,您指定的条件(谓词)告诉要保留的内容,而不是要过滤掉的内容!也就是说,当您返回true
时,该元素将出现在结果序列中,而当您返回false
时将不在结果序列中。 (如果您知道的话,就像 SQL 中的WHERE
条件.)
尽管filter
最常用于list directive,但自然可以在需要过滤序列的任何地方使用它,因此它也可以正常工作:
<#assign negatives = xs?filter(x -> x < 0)>
Negatives:
<#list negatives as x>${x} </#list>
但是请注意,对于很长的序列,上述解决方案可能会消耗更多的内存。这是因为<list seq?filter(pred) ...>
被优化为在不构建中间过滤序列的情况下进行过滤,而上面的示例assign
将首先在内存中构建整个过滤序列,然后将过滤后的序列传递给list
。但是同样,这仅在很长的序列中才有意义。
另请参阅:take_while built-in,drop_while built-in
Note:
相同的规则也适用于以下内置组件:map(mapper),take_while(predicate),drop_while(predicate)。
为了优化处理,filter
可能会延迟获取 Importing 序列的元素并将谓词应用于它们的延迟。但是可以保证这些操作不会延迟到指令或插值(其参数包含seq?filter(predicate)
)的执行完成的时间点。一些例子:
在<list seq?filter(predicate) ...>nested content</#list>
的情况下,当执行进入nested content
时,seq
的所有元素都已被使用和过滤并不是真的。消耗和过滤seq
的过程是逐点完成的,因为list
重复嵌套的内容。但是可以保证,在</#list>
标记之后(list
指令执行的结束),不会延迟读取seq
或延迟评估predicate
。因此,请避免在list
的嵌套内容中更改此类变量(或其他系统状态),这会影响predicate
的结果。这样做可能会更改seq
其余部分的过滤条件。
对于<#assign filteredSeq = seq?filter(predicate)>
,可以保证seq
的所有元素都已处理,因此assign
指令后不会对predicate
求值。
对于${seq?filter(predicate)?join(', ')}
,可以保证seq
的所有元素都已处理,因此assign
指令后不会对predicate
求值。
但是在expressions内部,对于何时使用元素以及何时对谓词进行评估没有任何保证。就像seq?filter(predicate1)?filter(predicate2)
一样,不能保证predicate1
仅在predicate2
之前被求值。 (很可能会交替调用它们:predicate1
表示第一个元素,然后predicate2
表示第一个元素,然后predicate1
表示第二个元素,然后predicate2
表示第二个元素,依此类推.)
如果您将经过过滤的序列传递给或myFunction(seq?filter(predicate))
中的* custom *指令(宏)或函数或方法,则可以确保在调用 custom 指令/函数/方法时,过滤不会延迟到该点之后。也就是说,您的宏/函数/方法将很快收到一个完整构造的过滤序列。
还要注意,其中不保证将读取 Importing 序列的所有元素,因此将对所有元素评估谓词。此类情况的一些示例:
您可以在<list seq?filter(predicate) ...>
到达最后一个元素之前从break中退出,在这种情况下,其余seq
元素将不会被提取和过滤。
对于seq?filter(predicate)[2]
,它读取已过滤序列的第 3 个元素,当我们找到与predicate
匹配的第 3 个元素时,FreeMarker 将停止获取和过滤seq
的元素。
在seq?filter(predicate)?size != 0
的情况下,它表明过滤后的序列是否为非空,当我们找到与predicate
匹配的第一个元素时,我们将停止获取和过滤seq
的元素。 (这确实令人惊讶,因为?size
需要处理整个序列来确定大小.但是在这种情况下,FreeMarker 注意到我们实际上并不需要确切的大小.)
如果您是 Java 程序员,请注意filter
内置与 Java Stream.filter
的区别。 Stream.filter
是“懒惰的”,而 FreeMarker filter
基本上是“渴望的”,并且仅在特殊情况下且在有限范围内才是“懒惰的”。因此,与 Java 不同,调用filter
并不总是免费的。特别是,如果您将过滤后的序列分配给变量,或将其传递给自定义指令/函数/方法,则过滤后的序列将被急切地创建。
Note:
相同的规则也适用于以下内置组件:map(mapper),take_while(predicate),drop_while(predicate)。
某些应用程序,特别是那些呈现巨大表的应用程序,在数据模型中使用了sequence-like values,但它们并未立即保存在内存中,相反,它们就像一串元素,您只能按照给定的 Sequences 读取它们(在在 Java 方面,它们是java.util.Iterator
-s 或java.util.Iterables
等)。它们在模板语言中将具有“集合”类型,就像受限序列一样。
filter
也可以使用集合 Importing。如您先前所见,filter
可能会将整个过滤后的序列存储在内存中,在这种情况下,这听起来很令人担忧,因为如果 Importing 太大而无法放入内存中(因此它不会作为序列公开),则过滤后的集合也可能太大。因此,如果 Importing 不是序列(而是集合),则filter
永远不会将其结果收集到内存中,也不会获取和处理 Importing 元素,直到 true 需要它们(“懒惰”行为)为止。此外,filter
的结果是一个集合,而不是序列,因此序列操作(如seq[index]
)将无法对其执行。
与序列 Importing 不同,任何将整个过滤结果收集到内存中的操作现在都会失败。让我们通过示例来看看。假设我们在数据模型中有hugeTable
,它不是一个序列,而是一个集合(在 Java 中可能是Iterator
)。然后,考虑:
<#-- Works: -->
<#list hugeTable?filter(predicate) as row>nested content</#list>
效果很好,因为list
不需要将结果收集到内存中
Consider this:
<#-- Fails if hugeTable is not a sequence, just a collection: -->
<#assign filteredHugeTable = hugeTable?filter(predicate)>
这失败了,因为过滤不能推迟到包含指令(assign
)之外,因此 FreeMareker 必须将整个过滤后的结果放入filteredHugeTable
。但是,如果您知道filteredHugeTable
不会太大,则可以通过sequence built-in将结果显式收集到序列中:
<#-- Works, but be sure filtredHugeTable fits into the memory: -->
<#assign filteredHugeTable = hugeTable?filter(predicate)?sequence>
自然地,应用内置的sequence
允许所有序列操作,例如seq[index]
,seq[range]
或seq?size
。如果将这些操作直接应用于从集合转换而来的序列,则 FreeMarker 会优化出实际在内存中创建序列的条件。因此,无论所过滤的hugeTable
的大小如何,它们都不会消耗太多内存:
hugeTable?filter(predicate)?sequence[index]
:FreeMarker 只会获取和删除元素,直到其到达所需位置的元素。
hugeTable?filter(predicate)?sequence[0..9]
:FreeMarker 只会收集前 10 个元素。
hugeTable?filter(predicate)?sequence?size
:在这种情况下,将提取整个hugeTable
,这可能很慢,但是所提取的元素仍未收集到内存中,因为它们只需要计数。
lambda 表达式的参数可以保留缺少的(Java null
)值,并且读取该值不会退回到更高的范围。因此,像seq?filter(it -> it??)
之类的东西会从序列中过滤掉丢失的元素,它将可靠地工作。
返回序列的第一项。因此value?first
与value[0]
相同,除了自 FreeMarker 2.3.26 起,如果value
不支持获取带有数字索引的项目,但仍支持列出(即,具有 FTL 集合值),则value?first
也可以工作。
如果序列或集合为空,则结果将为缺失值(如empty?first!'No item was found'
)。
使用给定的分隔符将序列的项目连接到单个字符串。例如:
<#assign colors = ["red", "green", "blue"]>
${colors?join(", ")}
will output:
red, green, blue
非字符串的序列项将转换为具有与${...}
相同的转换规则的字符串(当然,在此阶段不应用自动转义)。
?join(...)
最多可以包含 3 个参数:
必需的分隔符:项目之间插入的字符串
空值,默认为""
(空字符串):如果序列中不包含任何项目,则使用该值。
列表结尾,默认为""
(空字符串):如果列表序列不为空,则在最后一个值之后打印的值。
所以这(其中[]
表示一个空序列):
${colors?join(", ", "-")}
${[]?join(", ", "-")}
${colors?join(", ", "-", ".")}
${[]?join(", ", "-", ".")}
will output:
red, green, blue
red, green, blue.
来自 Java 的序列可能包含null
个值。这些值将被此内置函数忽略,就像将其从列表中删除一样。
序列的最后一个子变量。如果序列为空,则模板处理将因错误而终止。
返回一个新序列,其中所有元素都用参数 lambda,函数或方法的结果替换。例如,您在users
中有一个用户对象列表,但是您需要在变量中有一个用户名列表,那么您可以这样做:
<#assign userNames = users?map(user -> user.name)>
参数的工作方式类似于filter built-in的参数(因此请参见此处),不同之处在于您指定的 lambda/function/method 可以返回任何类型的值。
关于惰性评估和非常长的 Importing 的处理,它也将以相同的方式工作用作filter built-in。
返回序列(或集合)中较小的(min
)或最大的(max
)项。这些项目必须是所有数字,或者是同一类型的所有日期/时间值(仅日期,仅时间,日期时间),否则将发生比较错误。这些限制与\ < and >个运算符相同。
缺少的项目(即 Java null
-s)将被忽略。如果序列为空或仅包含缺少的(Java null
)项目,则结果本身将丢失。
Example:
${[1, 2, 3]?min}
${[1, 2, 3]?max}
${[]?min!'-'}
Sequences 颠倒。
Note:
内置名称中必须使用seq_
前缀,以区别于在字符串中搜索子字符串的contains built-in(因为变量可以同时是字符串和序列)。
告诉序列是否包含指定的值(根据模板语言的== operator,而不是 Java 的Object.equals
)。它有 1 个参数,即要查找的值。例:
<#assign x = ["red", 16, "blue", "cyan"]>
"blue": ${x?seq_contains("blue")?string("yes", "no")}
"yellow": ${x?seq_contains("yellow")?string("yes", "no")}
16: ${x?seq_contains(16)?string("yes", "no")}
"16": ${x?seq_contains("16")?string("yes", "no")}
输出将是:
"blue": yes
"yellow": no
16: yes
"16": no
要查找内置值,将使用 FreeMarker 的比较规则(就像您使用== operator一样),除了比较两个不同类型的值或 FreeMarker 不支持比较的类型的值不会导致错误,只是将其评估为这两个值不相等。因此,您只能使用它来查找标量值(即字符串,数字,布尔值或日期/时间值)。对于其他类型,结果将始终为false
。
为了容错,此内置函数也可以与集合一起使用。
Note:
从 FreeMarker 2.3.1 开始可以使用此内置功能。它在 2.3 中不存在。
Note:
内置名称中必须使用seq_
前缀,以区别于在字符串中搜索子字符串的index_of built-in(因为变量可以同时是字符串和序列)。
返回序列中值第一次出现的索引,如果序列不包含指定值,则返回-1
。要查找的值被指定为第一个参数。例如,此模板:
<#assign colors = ["red", "green", "blue"]>
${colors?seq_index_of("blue")}
${colors?seq_index_of("red")}
${colors?seq_index_of("purple")}
将输出以下内容:
要查找内置值,将使用 FreeMarker 的比较规则(就像您使用== operator一样),除了比较两个不同类型的值或 FreeMarker 不支持比较的类型的值不会导致错误,只是将其评估为这两个值不相等。因此,您只能使用它来查找标量值(即字符串,数字,布尔值或日期/时间值)。对于其他类型,结果将始终为-1
。
可以选择将开始搜索的索引作为第二个参数。如果同一项目可以相同的 Sequences 出现多次,这可能很有用。第二个参数的数值没有限制:如果为负数,则具有与零相同的效果;如果它大于序列的长度,则具有与第二个参数相同的效果。等于序列的长度。十进制值将被截断为整数。例如:
<#assign names = ["Joe", "Fred", "Joe", "Susan"]>
No 2nd param: ${names?seq_index_of("Joe")}
-2: ${names?seq_index_of("Joe", -2)}
-1: ${names?seq_index_of("Joe", -1)}
0: ${names?seq_index_of("Joe", 0)}
1: ${names?seq_index_of("Joe", 1)}
2: ${names?seq_index_of("Joe", 2)}
3: ${names?seq_index_of("Joe", 3)}
4: ${names?seq_index_of("Joe", 4)}
将输出以下内容:
No 2nd param: 0
-2: 0
-1: 0
3: -1
4: -1
Note:
从 FreeMarker 2.3.1 开始可以使用此内置功能。它在 2.3 中不存在。
Note:
内置名称中必须使用seq_
前缀,以区别于在字符串中搜索子字符串的last_index_of built-in(因为变量可以同时是字符串和序列)。
返回序列中最后一次出现的值的索引,如果序列不包含指定值,则返回-1
。也就是说,它与seq_index_of相同,只是它从序列的最后一项开始向后搜索。它还支持可选的 2nd 参数,该参数指定开始搜索的索引。例如:
<#assign names = ["Joe", "Fred", "Joe", "Susan"]>
No 2nd param: ${names?seq_last_index_of("Joe")}
-2: ${names?seq_last_index_of("Joe", -2)}
-1: ${names?seq_last_index_of("Joe", -1)}
0: ${names?seq_last_index_of("Joe", 0)}
1: ${names?seq_last_index_of("Joe", 1)}
2: ${names?seq_last_index_of("Joe", 2)}
3: ${names?seq_last_index_of("Joe", 3)}
4: ${names?seq_last_index_of("Joe", 4)}
将输出以下内容:
No 2nd param: 2
-2: -1
-1: -1
Sequences 中子变量的数量(作为数值)。假设序列具有至少一个子变量,则序列s
中的最高可能索引为s?size - 1
(因为第一个子变量的索引为 0)。
返回按升序排序的序列。 (对于降序使用,然后使用__.)仅在所有子变量都是字符串,或者所有子变量都是数字,或者所有子变量都是日期值(日期,时间或日期时间)时,此方法才有效,或者如果所有子变量均为布尔值(从 2.3.17 开始)。如果子变量是字符串,则使用特定于语言环境(语言)的词法排序(通常不区分大小写)。例如:
<#assign ls = ["whale", "Barbara", "zeppelin", "aardvark", "beetroot"]?sort>
<#list ls as i>${i} </#list>
将打印(至少在美国区域设置中):
aardvark Barbara beetroot whale zeppelin
返回按给定哈希子变量升序排序的哈希序列。 (对于降序使用,然后使用__.)规则与sort built-in相同,不同之处在于序列的子变量必须为哈希,并且您必须提供将决定 Sequences 的哈希子变量的名称。 。例如:
<#assign ls = [
{"name":"whale", "weight":2000},
{"name":"Barbara", "weight":53},
{"name":"zeppelin", "weight":-200},
{"name":"aardvark", "weight":30},
{"name":"beetroot", "weight":0.3}
Order by name:
<#list ls?sort_by("name") as i>
- ${i.name}: ${i.weight}
</#list>
Order by weight:
<#list ls?sort_by("weight") as i>
- ${i.name}: ${i.weight}
</#list>
将打印(至少在美国区域设置中):
Order by name:
- aardvark: 30
- Barbara: 53
- beetroot: 0.3
- whale: 2000
- zeppelin: -200
Order by weight:
- zeppelin: -200
- beetroot: 0.3
- aardvark: 30
- Barbara: 53
- whale: 2000
如果要用于排序的子变量位于更深的层次上(也就是说,如果它是子变量的子变量,依此类推),则可以使用序列作为参数,指定子变量的名称导致所需的子变量。例如:
<#assign members = [
{"name": {"first": "Joe", "last": "Smith"}, "age": 40},
{"name": {"first": "Fred", "last": "Crooger"}, "age": 35},
{"name": {"first": "Amanda", "last": "Fox"}, "age": 25}]>
Sorted by name.last:
<#list members?sort_by(['name', 'last']) as m>
- ${m.name.last}, ${m.name.first}: ${m.age} years old
</#list>
将打印(至少在美国区域设置中):
Sorted by name.last:
- Crooger, Fred: 35 years old
- Fox, Amanda: 25 years old
- Smith, Joe: 40 years old
返回一个序列,该序列仅包含 Importing 序列中位于第一个与参数谓词(过滤条件)不匹配的元素之前的元素。这与filter built-in非常相似,因此请在此处查看更多详细信息。
示例和与filter
的比较:
<#assign xs = [1, 2, -3, 4, -5, 6]>
Take while positive:
<#list xs?take_while(x -> x > 0) as x>${x} </#list>
Filer for positives:
<#list xs?filter(x -> x > 0) as x>${x} </#list>
Take while positive:
Filer for positives:
1 2 4 6
如您所见,take_while
停在与谓词不匹配的第一个数字(x > 0
),而filter
continue 寻找其他匹配项。
另请参阅:drop_while built-in