迭代器工具(简称itertools )是一组方便的工具,用于处理数据序列(例如数组、迭代器和字符串)。一些命名和 API 是基于 Python itertools的。
示例
常见的操作包括:
map
和mapBy
filter
、 difference
sorted
groupBy
accumulate
、 collapse
和reduce
为了通过 Twig 使用可用的itertools过滤器/函数,只需在config/services.yaml
中添加此服务定义
Zicht itertools twigExtension :
tags : ['twig.extension']
composer test
composer lint
下面的示例将使用以下数据来说明各种 Iterator 工具的工作原理:
$ words = [ ' Useful ' , ' Goonies ' , ' oven ' , ' Bland ' , ' notorious ' ];
$ numbers = [ 1 , 3 , 2 , 5 , 4 ];
$ vehicles = [
[
' id ' => 1 ,
' type ' => ' car ' ,
' wheels ' => 4 ,
' colors ' => [ ' red ' , ' green ' , ' blue ' ],
' is_cool ' => false ,
' price ' => 20000 ,
],
[
' id ' => 2 ,
' type ' => ' bike ' ,
' wheels ' => 2 ,
' colors ' => [ ' red ' , ' green ' , ' blue ' ],
' is_cool ' => false ,
' price ' => 600 ,
],
[
' id ' => 5 ,
' type ' => ' unicicle ' ,
' wheels ' => 1 ,
' colors ' => [ ' red ' ],
' is_cool ' => true ,
' price ' => 150 ,
],
[
' id ' => 9 ,
' type ' => ' car ' ,
' wheels ' => 8 ,
' colors ' => [ ' blue ' ],
' is_cool ' => true ,
' price ' => 100000 ,
],
];
通过上面的示例数据,您可以使用itertools按字母顺序获取汽车的所有独特颜色:
use Zicht itertools util Filters ;
use function Zicht itertools iterable ;
$ vehicles = iterable ( $ vehicles )
-> filter (Filters:: equals ( ' car ' , ' type ' )) // {[vehicle...], [vehicle...]}
-> map ( ' colors ' ) // {0: ['red', 'green', 'blue'], 1: ['blue']}
-> collapse () // {0: 'red', 1: 'green', 2: 'blue', 3: 'blue'}
-> unique () // {0: 'red', 1: 'green', 2: 'blue'}
-> sorted (); // {2: 'blue', 1: 'green', 0: 'red'}
您可以在 Twig 中实现相同的目标:
{% for vehicle_color in vehicles
|it. filter ( it . filters . equals ( ' car ' , ' type ' ))
|it. map ( ' colors ' )
|it. collapse
|it. unique
|it. sorted
%}
{{ vehicle_color }}
{% endfor %}
许多itertools都可以传递$strategy
参数。该参数用于从集合中的元素获取值。 $strategy
可以是以下三项之一:
null,在这种情况下返回元素本身。例如:
use function Zicht itertools iterable ;
$ result = iterable ( $ words )-> map ( null );
var_dump ( $ result );
// {0: 'Useful', 1: 'Goonies', 2: 'oven', 3: 'Bland', 4: 'notorious'}
或者在树枝中:
{{ dump ( word |it. map ) }}
一个闭包,在这种情况下,使用元素值和键作为参数来调用闭包,用于计算返回值。例如:
use function Zicht itertools iterable ;
$ getDouble = fn ( $ value , $ key ) => 2 * $ value ;
$ result = iterable ( $ numbers )-> map ( $ getDouble );
var_dump ( $ result );
// {0: 2, 1: 6, 2: 4, 3: 10, 4: 8}
或者在树枝中:
{{ dump ( numbers |it. map ( num => 2 * num )) }}
一个字符串,在这种情况下,该字符串用于创建一个尝试查找公共属性、方法或数组索引的闭包。例如:
use function Zicht itertools iterable ;
$ result = iterable ( $ vehicles )-> map ( ' type ' );
var_dump ( $ result );
// {0: 'car', 1: 'bike', 2: 'unicicle', 3: 'car'}
或者在树枝中:
{{ dump ( word |it. map ) }}
该字符串可以由多个点分隔的单词组成,允许访问嵌套的属性、方法和数组索引。
如果字符串中的某个单词无法解析为现有属性、方法或数组索引,则将返回值null
。例如:
use function Zicht itertools iterable ;
$ result = iterable ( $ vehicles )-> map ( ' colors.2 ' );
var_dump ( $ result );
// {0: 'blue', 1: 'blue', 2: null, 3: null}
或者在树枝中:
{{ dump ( vehicles |it. map ( ' colors.2 ' )) }}
使用迭代器工具的一种方法是将数组、迭代器、字符串等转换为IterableIterator
。此类为所有常见操作提供了流畅的接口。例如:
use function Zicht itertools iterable ;
$ result = iterable ( $ vehicles )-> filter ( ' is_cool ' )-> mapBy ( ' id ' )-> map ( ' type ' );
var_dump ( $ result );
// {5: 'unicicle', 9: 'car'}
或者在树枝中:
{{ dump ( vehicles |it. filter ( ' is_cool ' ).mapBy( ' id ' ).map( ' type ' )) }}
映射将一个集合转换为另一个长度相等的集合。使用map
允许操作元素,而mapBy
允许操作集合键。
例如,我们可以使用闭包为$vehicles
中的每个元素创建一个标题:
use function Zicht itertools iterable ;
$ getTitle = fn ( $ value , $ key ) => sprintf ( ' %s with %s wheels ' , $ value [ ' type ' ], $ value [ ' wheels ' ]);
$ titles = iterable ( $ vehicles )-> map ( $ getTitle );
var_dump ( $ titles );
// {0: 'car with 4 wheels', ..., 3: 'car with 8 wheels'}
使用字符串 getter 策略,我们可以轻松获取由车辆标识符映射的$vehicles
中每个元素的类型。例如:
use function Zicht itertools iterable ;
$ types = iterable ( $ vehicles )-> mapBy ( ' id ' )-> map ( ' type ' );
var_dump ( $ types );
// {1: 'car', 2: 'bike', 5: 'unicicle', 9: 'car'}
或者在树枝中:
{{ dump ( vehicles |it. mapBy ( ' id ' ).map( ' type ' )) }}
在mappings.php 中有几个常见的映射闭包。调用这些函数会返回一个可以传递给map
和mapBy
闭包。例如:
use Zicht itertools util Mappings ;
use function Zicht itertools iterable ;
$ lengths = iterable ( $ words )-> map (Mappings:: length ());
var_dump ( $ lengths );
// {0: 6, 1: 3, 2: 4, 3: 5, 4: 9}
或者在树枝中:
{{ dump ( words |it. map ( it . mappings . length )) }}
过滤将一个集合转换为另一个可能更短的集合。使用filter
对集合中的每个元素进行评估,被视为empty
元素将被拒绝,而不是empty
元素将被允许通过过滤器。
例如,我们可以使用闭包来确定某个元素是否昂贵,然后filter
将只允许昂贵的元素通过:
use function Zicht itertools iterable ;
$ isExpensive = fn ( $ value , $ key ) => $ value [ ' price ' ] >= 10000 ;
$ expensiveTypes = iterable ( $ vehicles )-> filter ( $ isExpensive )-> map ( ' type ' );
var_dump ( $ expensiveTypes );
// {1: 'car', 9: 'car'}
或者在树枝中:
{{ dump ( vehicles |it. filter ( vehicle => vehicle . price >= 10000 ).map( ' type ' )) }}
使用字符串 getter 策略,我们只能获取被认为很酷的$vehicles
。例如:
use function Zicht itertools iterable ;
$ coolVehicleTypes = iterable ( $ vehicles )-> filter ( ' is_cool ' )-> map ( ' type ' );
var_dump ( $ coolVehicleTypes );
// {5: 'unicicle', 9: 'car'}
或者在树枝中:
{{ dump ( vehicles |it. filter ( ' is_cool ' ).map( ' type ' )) }}
filters.php 中有几种常见的过滤器闭包。调用这些函数会返回一个可以传递给filter
闭包。例如:
use Zicht itertools util Filters ;
use function Zicht itertools iterable ;
$ movieWords = iterable ( $ words )-> filter (Filters:: in ([ ' Shining ' , ' My little pony ' , ' Goonies ' ]));
var_dump ( $ movieWords );
// {1: 'Goonies'}
或者在树枝中:
{{ dump ( words |it. filter ( it . filters . in ([ ' Shining ' , " My little pony', 'Goonies'])) }}
sorted
将一个集合转换为另一个大小相等但元素可能重新排序的集合。
例如,使用默认的null
getter 策略,我们将使用元素值按升序进行排序:
use function Zicht itertools iterable ;
$ ordered = iterable ( $ numbers )-> sorted ();
var_dump ( $ ordered );
// {0: 1, 2: 2, 1: 3, 4: 4, 3: 5}
或者在树枝中:
{{ dump ( numbers |it. sorted }}
排序算法将保留键并保证稳定。即,当使用相同值对元素进行排序时,则保证排序顺序与输入元素的顺序相同。这与标准的 PHP 排序函数相反。
使用闭包 getter 策略,返回值用于确定顺序。每个元素只调用一次闭包,并且结果值必须是可比较的。例如:
use function Zicht itertools iterable ;
$ getLower = fn ( $ value , $ key ) => strtolower ( $ value );
$ ordered = iterable ( $ words )-> sorted ( $ getLower );
var_dump ( $ ordered );
// {3: 'Bland', 1: 'Goonies', 2: 'oven', 0: 'Useful', 4: 'notorious'};
mappings.php 提供了一个映射闭包,它返回一个随机数。这可用于以随机顺序对集合进行排序。例如:
use Zicht itertools util Mappings ;
use function Zicht itertools iterable ;
$ randomized = iterable ( $ words )-> sorted (Mappings:: random ());
var_dump ( $ randomized );
// {... randomly ordere words ...}
或者在树枝中:
{{ dump ( words |it. sorted ( it . mappings . random )) }}
groupBy
将一个集合转换为一个或多个集合,这些集合根据特定条件将元素分组在一起。
例如,使用字符串 getter 策略,我们可以将所有相同类型的$vehicles
分组在一起:
use function Zicht itertools iterable ;
$ vehiclesByType = iterable ( $ vehicles )-> groupBy ( ' type ' );
var_dump ( $ vehiclesByType );
// {'bike': {1: [...]}, 'car': {0: [...], 3: [...]} 'unicicle': {2: [...]}}
或者在树枝中:
{{ dump ( vehicles |it. groupBy ( ' type ' )) }}
并不是说车辆的原始键仍然是结果组的一部分,并且每个组中的元素保持它们在输入中的顺序,即它使用sorted
提供的稳定排序。
reduce
通过从左到右对集合中的元素累积调用两个参数的闭包,将集合转换为单个值。
例如,在没有任何参数的情况下, reduce
会将集合的所有元素添加在一起:
use function Zicht itertools iterable ;
$ sum = iterable ( $ numbers )-> reduce ();
var_dump ( $ sum );
// 15
或者在树枝中:
{{ dump ( numbers |it. reduce ) }}
在上面的示例中,使用的默认闭包如下所示:
public static function add ( $ a , $ b ): Closure
{
return $ a + $ b ;
}
假设$numbers
由元素 {1, 3, 2, 5, 4} 组成, add
闭包被调用四次:
$ sum = Reductions:: add (Reductions:: add (Reductions:: add (Reductions:: add (( 1 , 3 ), 2 ), 5 ), 4 ));
var_dump ( $ sum );
// 15
reductions.php 中有几种常见的归约闭包。调用这些函数会返回一个可以传递给reduction
的闭包。例如:
use Zicht itertools util Reductions ;
use function Zicht itertools iterable ;
$ scentence = iterable ( $ words )-> reduce (Reductions:: join ( ' - ' ));
var_dump ( $ scentence );
// 'Useful - Goonies - oven - Bland - notorious'
或者在树枝中:
{{ dump ( words |it. reduce ( it . reductions . join ( ' - ' )) }}
另一种常见的简化是将多个列表链接到一个列表中。我们称这个过程为崩溃。这个过程也可以使用reduce
和chain
一起实现,但是,由于它被频繁使用,所以collapse
助手使它的使用更容易,例如:
use function Zicht itertools iterable ;
$ flat = iterable ([[ ' one ' , ' two ' ], [ ' three ' ]])-> collapse ();
var_dump ( $ flat );
// {0: 'one', 1: 'two', 0: 'three'}
或者在树枝中:
{% set data = [[ ' one ' , ' two ' ], [ ' three ' ]] %}
{{ dump ( data |it. collapse ) }}