Las herramientas de iterador, o itertools para abreviar, son una colección de herramientas convenientes para manejar secuencias de datos como matrices, iteradores y cadenas. Parte de los nombres y API se basan en las itertools de Python.
Ejemplos
Las operaciones comunes incluyen:
map
y mapBy
filter
, difference
sorted
groupBy
accumulate
, collapse
y reduce
Para utilizar los filtros/funciones itertools disponibles a través de Twig, simplemente agregue esta definición de servicio en su config/services.yaml
Zicht itertools twigExtension :
tags : ['twig.extension']
composer test
composer lint
Los siguientes ejemplos utilizarán los siguientes datos para ilustrar cómo funcionan varias herramientas 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 ,
],
];
Con los datos de ejemplo anteriores, así es como puedes usar itertools para obtener todos los colores únicos de los autos en orden alfabético:
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'}
Puedes lograr lo mismo en 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 %}
A muchas itertools se les puede pasar un parámetro $strategy
. Este parámetro se utiliza para obtener un valor de los elementos de la colección. La $strategy
puede ser una de tres cosas:
null, en cuyo caso se devuelve el elemento en sí. Por ejemplo:
use function Zicht itertools iterable ;
$ result = iterable ( $ words )-> map ( null );
var_dump ( $ result );
// {0: 'Useful', 1: 'Goonies', 2: 'oven', 3: 'Bland', 4: 'notorious'}
O en ramita:
{{ dump ( word |it. map ) }}
un cierre, en cuyo caso el cierre se llama con el valor del elemento y la clave como parámetros que se utilizarán para calcular un valor de retorno. Por ejemplo:
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}
O en ramita:
{{ dump ( numbers |it. map ( num => 2 * num )) }}
una cadena, en cuyo caso esta cadena se utiliza para crear un cierre que intenta encontrar propiedades públicas, métodos o índices de matriz. Por ejemplo:
use function Zicht itertools iterable ;
$ result = iterable ( $ vehicles )-> map ( ' type ' );
var_dump ( $ result );
// {0: 'car', 1: 'bike', 2: 'unicicle', 3: 'car'}
O en ramita:
{{ dump ( word |it. map ) }}
La cadena puede constar de varias palabras separadas por puntos, lo que permite el acceso a propiedades, métodos e índices de matriz anidados.
Si una de las palabras de la cadena no se puede resolver en una propiedad, método o índice de matriz existente, se devolverá el valor null
. Por ejemplo:
use function Zicht itertools iterable ;
$ result = iterable ( $ vehicles )-> map ( ' colors.2 ' );
var_dump ( $ result );
// {0: 'blue', 1: 'blue', 2: null, 3: null}
O en ramita:
{{ dump ( vehicles |it. map ( ' colors.2 ' )) }}
Una forma de utilizar las herramientas de iterador es convertir la matriz, el iterador, la cadena, etc. en un IterableIterator
. Esta clase proporciona una interfaz fluida para todas las operaciones comunes. Por ejemplo:
use function Zicht itertools iterable ;
$ result = iterable ( $ vehicles )-> filter ( ' is_cool ' )-> mapBy ( ' id ' )-> map ( ' type ' );
var_dump ( $ result );
// {5: 'unicicle', 9: 'car'}
O en ramita:
{{ dump ( vehicles |it. filter ( ' is_cool ' ).mapBy( ' id ' ).map( ' type ' )) }}
El mapeo convierte una colección en otra colección de igual longitud. El uso de map
permite la manipulación de los elementos, mientras que mapBy
permite la manipulación de las claves de colección.
Por ejemplo, podemos usar un cierre para crear un título para cada elemento en $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'}
Usando la estrategia de obtención de cadenas, podemos obtener fácilmente los tipos de cada elemento en $vehicles
asignados por los identificadores de vehículos. Por ejemplo:
use function Zicht itertools iterable ;
$ types = iterable ( $ vehicles )-> mapBy ( ' id ' )-> map ( ' type ' );
var_dump ( $ types );
// {1: 'car', 2: 'bike', 5: 'unicicle', 9: 'car'}
O en ramita:
{{ dump ( vehicles |it. mapBy ( ' id ' ).map( ' type ' )) }}
Hay varios cierres de mapeo comunes disponibles en mapeos.php. Llamar a estas funciones devuelve un cierre que se puede pasar a map
y mapBy
. Por ejemplo:
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}
O en ramita:
{{ dump ( words |it. map ( it . mappings . length )) }}
El filtrado convierte una colección en otra colección, posiblemente más corta. Usando filter
se evalúa cada elemento de la colección, los elementos que se consideren empty
serán rechazados, mientras que los elementos que no están empty
podrán pasar por el filtro.
Por ejemplo, podemos usar un cierre para determinar si un elemento es caro, el filter
entonces solo permitirá el paso de los elementos caros:
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'}
O en ramita:
{{ dump ( vehicles |it. filter ( vehicle => vehicle . price >= 10000 ).map( ' type ' )) }}
Usando la estrategia de obtención de cadenas, podemos obtener solo los $vehicles
que se consideran geniales. Por ejemplo:
use function Zicht itertools iterable ;
$ coolVehicleTypes = iterable ( $ vehicles )-> filter ( ' is_cool ' )-> map ( ' type ' );
var_dump ( $ coolVehicleTypes );
// {5: 'unicicle', 9: 'car'}
O en ramita:
{{ dump ( vehicles |it. filter ( ' is_cool ' ).map( ' type ' )) }}
Hay varios cierres de filtros comunes disponibles en filters.php. Llamar a estas funciones devuelve un cierre que se puede pasar al filter
. Por ejemplo:
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'}
O en ramita:
{{ dump ( words |it. filter ( it . filters . in ([ ' Shining ' , " My little pony', 'Goonies'])) }}
sorted
convierte una colección en otra colección del mismo tamaño pero con los elementos posiblemente reordenados.
Por ejemplo, usando la estrategia de obtención null
, que es la predeterminada, ordenaremos usando los valores de los elementos en orden ascendente:
use function Zicht itertools iterable ;
$ ordered = iterable ( $ numbers )-> sorted ();
var_dump ( $ ordered );
// {0: 1, 2: 2, 1: 3, 4: 4, 3: 5}
O en ramita:
{{ dump ( numbers |it. sorted }}
El algoritmo de clasificación preservará las claves y se garantiza que será estable. Es decir, cuando los elementos se ordenan utilizando el mismo valor, se garantiza que el orden de clasificación será el mismo que el orden de los elementos de entrada. Esto es contrario a las funciones de clasificación estándar de PHP.
Usando la estrategia de obtención de cierre, el valor devuelto se usa para determinar el orden. El cierre se llama exactamente una vez por elemento y los valores resultantes deben ser comparables. Por ejemplo:
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 proporciona un cierre de mapeo que devuelve un número aleatorio. Esto se puede utilizar para ordenar una colección en orden aleatorio. Por ejemplo:
use Zicht itertools util Mappings ;
use function Zicht itertools iterable ;
$ randomized = iterable ( $ words )-> sorted (Mappings:: random ());
var_dump ( $ randomized );
// {... randomly ordere words ...}
O en ramita:
{{ dump ( words |it. sorted ( it . mappings . random )) }}
groupBy
convierte una colección en una o más colecciones que agrupan los elementos según un criterio específico.
Por ejemplo, usando la estrategia string getter podemos agrupar todos los $vehicles
del mismo tipo:
use function Zicht itertools iterable ;
$ vehiclesByType = iterable ( $ vehicles )-> groupBy ( ' type ' );
var_dump ( $ vehiclesByType );
// {'bike': {1: [...]}, 'car': {0: [...], 3: [...]} 'unicicle': {2: [...]}}
O en ramita:
{{ dump ( vehicles |it. groupBy ( ' type ' )) }}
No es que las claves originales de los vehículos sigan siendo parte de los grupos resultantes, y los elementos dentro de cada grupo mantienen el orden que tenían en la entrada, es decir, utiliza la clasificación estable proporcionada por sorted
.
reduce
convierte una colección en un valor único llamando a un cierre de dos argumentos de forma acumulativa para los elementos de la colección, de izquierda a derecha.
Por ejemplo, sin ningún argumento, reduce
agregará todos los elementos de la colección:
use function Zicht itertools iterable ;
$ sum = iterable ( $ numbers )-> reduce ();
var_dump ( $ sum );
// 15
O en ramita:
{{ dump ( numbers |it. reduce ) }}
En el ejemplo anterior, el cierre predeterminado que se utiliza tiene este aspecto:
public static function add ( $ a , $ b ): Closure
{
return $ a + $ b ;
}
Dado que $numbers
consta de los elementos {1, 3, 2, 5, 4}, el cierre add
se llama cuatro veces:
$ sum = Reductions:: add (Reductions:: add (Reductions:: add (Reductions:: add (( 1 , 3 ), 2 ), 5 ), 4 ));
var_dump ( $ sum );
// 15
Hay varios cierres de reducción comunes disponibles en reducciones.php. Llamar a estas funciones devuelve un cierre que se puede pasar a reduction
. Por ejemplo:
use Zicht itertools util Reductions ;
use function Zicht itertools iterable ;
$ scentence = iterable ( $ words )-> reduce (Reductions:: join ( ' - ' ));
var_dump ( $ scentence );
// 'Useful - Goonies - oven - Bland - notorious'
O en ramita:
{{ dump ( words |it. reduce ( it . reductions . join ( ' - ' )) }}
Otra reducción común es encadenar varias listas en una sola. A este proceso lo llamamos colapso. Este proceso también se puede lograr usando reduce
y chain
juntos; sin embargo, debido a que se usa con frecuencia, el asistente collapse
facilita su uso, por ejemplo:
use function Zicht itertools iterable ;
$ flat = iterable ([[ ' one ' , ' two ' ], [ ' three ' ]])-> collapse ();
var_dump ( $ flat );
// {0: 'one', 1: 'two', 0: 'three'}
O en ramita:
{% set data = [[ ' one ' , ' two ' ], [ ' three ' ]] %}
{{ dump ( data |it. collapse ) }}