zicht / itertools
一组函数,用于模拟 Python itertools 库
Requires
- php: ^7.4 || ^8.0
Requires (Dev)
- doctrine/collections: ^1.3
- phpunit/phpunit: ^9
- rregeer/phpunit-coverage-check: ^0.1
- twig/twig: ^2.0 || ^3.0
- zicht/standards-php: ^4.0
- dev-release/3.x
- dev-release/2.x
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 2.15.0
- 2.14.0
- 2.13.0
- 2.12.3
- 2.12.2
- 2.12.1
- 2.12.0
- 2.11.0
- 2.10.14
- 2.10.13
- 2.10.12
- 2.10.11
- 2.10.10
- 2.10.9
- 2.10.8
- 2.10.7
- 2.10.6
- 2.10.5
- 2.10.4
- 2.10.3
- 2.10.2
- 2.10.1
- 2.10.0
- 2.10.0-rc.3
- 2.10.0-rc.2
- 2.10.0-rc.1
- 2.9.0
- 2.8.26
- 2.8.25
- 2.8.24
- 2.8.23
- 2.8.22-rc.1
- 2.8.21
- 2.8.20
- 2.8.19
- 2.8.18
- 2.8.17
- 2.8.16
- 2.8.15
- 2.8.14
- 2.8.13
- 2.8.12
- 2.8.11
- 2.8.10
- 2.8.9
- 2.8.8
- 2.8.7
- 2.8.6
- 2.8.5
- 2.8.4
- 2.8.3
- 2.8.2
- 2.8.1
- 2.8.0
- 2.7.1
- 2.7.0
- 2.6.0
- 2.5.3
- 2.5.2
- 2.5.1
- 2.5.0
- 2.4.0
- 2.3.1
- 2.3.1-rc.5
- 2.3.1-rc.4
- 2.3.1-rc.3
- 2.3.1-rc.2
- 2.3.1-rc.1
- 2.3.0
- 2.3.0-beta.3
- 2.3.0-beta.2
- 2.3.0-beta.1
- 2.2.0-beta.1
- 2.1.0-beta.3
- 2.1.0-beta.2
- 2.1.0-beta.1
- 2.0.0-beta.2
- 2.0.0-beta.1
- 1.5.0
- 1.5.0-beta.5
- 1.5.0-beta.4
- 1.5.0-beta.3
- 1.5.0-beta.2
- 1.5.0-beta.1
- 1.4.3
- 1.4.2
- 1.4.1
- 1.4.1-rc.2
- 1.4.1-rc.1
- 1.4.0
- 1.4.0-rc.2
- 1.4.0-rc.1
- 1.3.0
- 1.2.0
- 1.2.0-beta.1
- 1.1.0
- 1.1.0-beta.1
- 1.0.0-beta.1
- 1.0.0-alpha.2
- 1.0.0-alpha.1
This package is auto-updated.
Last update: 2024-09-08 10:13:23 UTC
README
Zicht 迭代器工具库
迭代器工具,简称 itertools,是一组方便的工具,用于处理数据序列,如数组、迭代器和字符串。部分命名和 API 基于 Python itertools。
常见操作包括
用法
要使用 Twig 通过可用的 itertools 过滤器/函数,只需在您的 config/services.yaml
中添加此服务定义
Zicht\Itertools\twig\Extension: tags: ['twig.extension']
脚本
- 单元测试:
composer test
- 代码风格检查测试:
composer lint
示例数据
以下示例将使用以下数据来展示各种迭代器工具的工作原理
$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'}
或者在 Twig 中
{{ 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}
或者在 Twig 中
{{ 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'}
或者在 Twig 中
{{ 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}
或者在 Twig 中
{{ 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'}
或者在 Twig 中
{{ 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'}
使用 获取策略,我们可以轻松获取由车辆标识符映射的 $vehicles
中每个元素的类型。例如
use function Zicht\Itertools\iterable; $types = iterable($vehicles)->mapBy('id')->map('type'); var_dump($types); // {1: 'car', 2: 'bike', 5: 'unicicle', 9: 'car'}
或者在 Twig 中
{{ 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}
或者在 Twig 中
{{ dump(words|it.map(it.mappings.length)) }}
过滤
过滤操作将一个集合转换成另一个可能的更短的集合。使用 filter
,集合中的每个元素都会被评估,被认为是 空
的元素将被拒绝,而那些不是 空
的元素则可以通过过滤器。
例如,我们可以使用闭包来判断一个元素是否昂贵,然后 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'}
或者在 Twig 中
{{ 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'}
或者在 Twig 中
{{ 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'}
或者在 Twig 中
{{ dump(words|it.filter(it.filters.in(['Shining', "My little pony', 'Goonies'])) }}
排序
sorted
将一个集合转换成另一个大小相同但元素可能重新排序的集合。
例如,使用默认的 getter 策略(即 null
),我们将按照元素的值进行升序排序。
use function Zicht\Itertools\iterable; $ordered = iterable($numbers)->sorted(); var_dump($ordered); // {0: 1, 2: 2, 1: 3, 4: 4, 3: 5}
或者在 Twig 中
{{ 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 ...}
或者在 Twig 中
{{ 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: [...]}}
或者在 Twig 中
{{ dump(vehicles|it.groupBy('type')) }}
请注意,原始的车辆键仍然是结果组的一部分,并且每个组内的元素保持其在输入中的顺序,即它使用由 sorted
提供的稳定排序。
归约
reduce
通过累积调用集合中元素的闭包(两个参数)从左到右将一个集合转换成一个单一值。
例如,没有参数时,reduce
会将集合的所有元素相加。
use function Zicht\Itertools\iterable; $sum = iterable($numbers)->reduce(); var_dump($sum); // 15
或者在 Twig 中
{{ 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'
或者在 Twig 中
{{ 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'}
或者在 Twig 中
{% set data = [['one', 'two'], ['three']] %} {{ dump(data|it.collapse) }}
维护者
- Boudewijn Schoon boudewijn@zicht.nl
- Virginia Meijer virginia@zicht.nl