Les outils itérateurs, ou itertools en abrégé, sont une collection d'outils pratiques permettant de gérer des séquences de données telles que des tableaux, des itérateurs et des chaînes. Une partie de la dénomination et de l'API est basée sur les itertools Python.
Exemples
Les opérations courantes comprennent :
map
et mapBy
filter
, difference
sorted
groupBy
accumulate
, collapse
et reduce
Afin d'utiliser les filtres/fonctions itertools disponibles via Twig, ajoutez simplement cette définition de service dans votre config/services.yaml
Zicht itertools twigExtension :
tags : ['twig.extension']
composer test
composer lint
Les exemples ci-dessous utiliseront les données suivantes pour illustrer le fonctionnement des différents outils 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 ,
],
];
Avec les exemples de données ci-dessus, voici comment vous pouvez utiliser itertools pour obtenir toutes les couleurs uniques des voitures par ordre alphabétique :
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'}
Vous pouvez réaliser la même chose dans 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 %}
De nombreux itertools peuvent recevoir un paramètre $strategy
. Ce paramètre est utilisé pour obtenir une valeur à partir des éléments de la collection. La $strategy
peut être l’une des trois choses suivantes :
null, auquel cas l'élément lui-même est renvoyé. Par exemple:
use function Zicht itertools iterable ;
$ result = iterable ( $ words )-> map ( null );
var_dump ( $ result );
// {0: 'Useful', 1: 'Goonies', 2: 'oven', 3: 'Bland', 4: 'notorious'}
Ou dans Brindille :
{{ dump ( word |it. map ) }}
une fermeture, auquel cas la fermeture est appelée avec la valeur de l'élément et la clé comme paramètres à utiliser pour calculer une valeur de retour. Par exemple:
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}
Ou dans Brindille :
{{ dump ( numbers |it. map ( num => 2 * num )) }}
une chaîne, auquel cas cette chaîne est utilisée pour créer une fermeture qui tente de trouver des propriétés publiques, des méthodes ou des index de tableau. Par exemple:
use function Zicht itertools iterable ;
$ result = iterable ( $ vehicles )-> map ( ' type ' );
var_dump ( $ result );
// {0: 'car', 1: 'bike', 2: 'unicicle', 3: 'car'}
Ou dans Brindille :
{{ dump ( word |it. map ) }}
La chaîne peut être constituée de plusieurs mots séparés par des points, permettant l'accès aux propriétés, méthodes et index de tableau imbriqués.
Si l'un des mots de la chaîne ne peut pas être résolu en une propriété, une méthode ou un index de tableau existant, la valeur null
sera renvoyée. Par exemple:
use function Zicht itertools iterable ;
$ result = iterable ( $ vehicles )-> map ( ' colors.2 ' );
var_dump ( $ result );
// {0: 'blue', 1: 'blue', 2: null, 3: null}
Ou dans Brindille :
{{ dump ( vehicles |it. map ( ' colors.2 ' )) }}
Une façon d'utiliser les outils Iterator consiste à convertir le tableau, l'Iterator, la chaîne, etc. en IterableIterator
. Cette classe fournit une interface fluide pour toutes les opérations courantes. Par exemple:
use function Zicht itertools iterable ;
$ result = iterable ( $ vehicles )-> filter ( ' is_cool ' )-> mapBy ( ' id ' )-> map ( ' type ' );
var_dump ( $ result );
// {5: 'unicicle', 9: 'car'}
Ou dans Brindille :
{{ dump ( vehicles |it. filter ( ' is_cool ' ).mapBy( ' id ' ).map( ' type ' )) }}
Le mappage convertit une collection en une autre collection de longueur égale. L'utilisation map
permet la manipulation des éléments tandis que mapBy
permet la manipulation des clés de collection.
Par exemple, nous pouvons utiliser une fermeture pour créer un titre pour chaque élément dans $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'}
En utilisant la stratégie de lecture de chaînes, nous pouvons facilement obtenir les types de chaque élément de $vehicles
mappés par les identifiants de véhicule. Par exemple:
use function Zicht itertools iterable ;
$ types = iterable ( $ vehicles )-> mapBy ( ' id ' )-> map ( ' type ' );
var_dump ( $ types );
// {1: 'car', 2: 'bike', 5: 'unicicle', 9: 'car'}
Ou dans Brindille :
{{ dump ( vehicles |it. mapBy ( ' id ' ).map( ' type ' )) }}
Il existe plusieurs fermetures de mappage courantes disponibles dans mappings.php. L'appel de ces fonctions renvoie une fermeture qui peut être transmise à map
et mapBy
. Par exemple:
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}
Ou dans Brindille :
{{ dump ( words |it. map ( it . mappings . length )) }}
Le filtrage convertit une collection en une autre collection, éventuellement plus courte. En utilisant filter
chaque élément de la collection est évalué, les éléments considérés empty
seront rejetés, tandis que les éléments qui ne sont pas empty
seront autorisés à passer à travers le filtre.
Par exemple, on peut utiliser une fermeture pour déterminer si un élément est cher, le filter
ne laissera alors passer que les éléments chers :
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'}
Ou dans Brindille :
{{ dump ( vehicles |it. filter ( vehicle => vehicle . price >= 10000 ).map( ' type ' )) }}
En utilisant la stratégie de lecture de chaînes, nous pouvons obtenir uniquement les $vehicles
considérés comme cool. Par exemple:
use function Zicht itertools iterable ;
$ coolVehicleTypes = iterable ( $ vehicles )-> filter ( ' is_cool ' )-> map ( ' type ' );
var_dump ( $ coolVehicleTypes );
// {5: 'unicicle', 9: 'car'}
Ou dans Brindille :
{{ dump ( vehicles |it. filter ( ' is_cool ' ).map( ' type ' )) }}
Il existe plusieurs fermetures de filtres courantes disponibles dans filters.php. L’appel de ces fonctions renvoie une fermeture qui peut être transmise à filter
. Par exemple:
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'}
Ou dans Brindille :
{{ dump ( words |it. filter ( it . filters . in ([ ' Shining ' , " My little pony', 'Goonies'])) }}
sorted
convertit une collection en une autre collection de taille égale mais avec les éléments éventuellement réorganisés.
Par exemple, en utilisant la stratégie null
getter, qui est la stratégie par défaut, nous trierons en utilisant les valeurs des éléments par ordre croissant :
use function Zicht itertools iterable ;
$ ordered = iterable ( $ numbers )-> sorted ();
var_dump ( $ ordered );
// {0: 1, 2: 2, 1: 3, 4: 4, 3: 5}
Ou dans Brindille :
{{ dump ( numbers |it. sorted }}
L'algorithme de tri préservera les clés et est garanti stable. C'est-à-dire que lorsque les éléments sont triés en utilisant la même valeur, l'ordre de tri est garanti être le même que l'ordre des éléments d'entrée. Ceci est contraire aux fonctions de tri standard de PHP.
En utilisant la stratégie de lecture de fermeture, la valeur renvoyée est utilisée pour déterminer l'ordre. La fermeture est appelée exactement une fois par élément et les valeurs résultantes doivent être comparables. Par exemple:
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'};
Le mappings.php fournit une fermeture de mappage qui renvoie un nombre aléatoire. Cela peut être utilisé pour trier une collection dans un ordre aléatoire. Par exemple:
use Zicht itertools util Mappings ;
use function Zicht itertools iterable ;
$ randomized = iterable ( $ words )-> sorted (Mappings:: random ());
var_dump ( $ randomized );
// {... randomly ordere words ...}
Ou dans Brindille :
{{ dump ( words |it. sorted ( it . mappings . random )) }}
groupBy
convertit une collection en une ou plusieurs collections qui regroupent les éléments selon un critère spécifique.
Par exemple, en utilisant la stratégie de lecture de chaînes, nous pouvons regrouper tous les $vehicles
du même type :
use function Zicht itertools iterable ;
$ vehiclesByType = iterable ( $ vehicles )-> groupBy ( ' type ' );
var_dump ( $ vehiclesByType );
// {'bike': {1: [...]}, 'car': {0: [...], 3: [...]} 'unicicle': {2: [...]}}
Ou dans Brindille :
{{ dump ( vehicles |it. groupBy ( ' type ' )) }}
Non pas que les clés d'origine des véhicules fassent toujours partie des groupes résultants, et les éléments de chaque groupe conservent l'ordre qu'ils avaient en entrée, c'est-à-dire qu'il utilise le tri stable fourni par sorted
.
reduce
convertit une collection en une valeur unique en appelant une fermeture de deux arguments cumulativement sur les éléments de la collection, de gauche à droite.
Par exemple, sans aucun argument, reduce
ajoutera tous les éléments de la collection :
use function Zicht itertools iterable ;
$ sum = iterable ( $ numbers )-> reduce ();
var_dump ( $ sum );
// 15
Ou dans Brindille :
{{ dump ( numbers |it. reduce ) }}
Dans l'exemple ci-dessus, la fermeture par défaut utilisée ressemble à ceci :
public static function add ( $ a , $ b ): Closure
{
return $ a + $ b ;
}
Étant donné que $numbers
est constitué des éléments {1, 3, 2, 5, 4}, la fermeture add
est appelée quatre fois :
$ sum = Reductions:: add (Reductions:: add (Reductions:: add (Reductions:: add (( 1 , 3 ), 2 ), 5 ), 4 ));
var_dump ( $ sum );
// 15
Il existe plusieurs fermetures de réduction courantes disponibles dans réductions.php. L'appel de ces fonctions renvoie une fermeture qui peut être transmise à reduction
. Par exemple:
use Zicht itertools util Reductions ;
use function Zicht itertools iterable ;
$ scentence = iterable ( $ words )-> reduce (Reductions:: join ( ' - ' ));
var_dump ( $ scentence );
// 'Useful - Goonies - oven - Bland - notorious'
Ou dans Brindille :
{{ dump ( words |it. reduce ( it . reductions . join ( ' - ' )) }}
Une autre réduction courante consiste à enchaîner plusieurs listes en une seule. Nous appelons ce processus l’effondrement. Ce processus peut également être réalisé en utilisant reduce
et chain
ensemble, cependant, comme il est fréquemment utilisé, l'assistant collapse
rend son utilisation plus facile, par exemple :
use function Zicht itertools iterable ;
$ flat = iterable ([[ ' one ' , ' two ' ], [ ' three ' ]])-> collapse ();
var_dump ( $ flat );
// {0: 'one', 1: 'two', 0: 'three'}
Ou dans Brindille :
{% set data = [[ ' one ' , ' two ' ], [ ' three ' ]] %}
{{ dump ( data |it. collapse ) }}