Estoy muy contento de que se haya introducido PHP 7.1 el pseudotipo iterable.
Ahora bien, si bien esto es excelente cuando simplemente se repite un parámetro de este tipo, no me queda claro qué hacer cuando necesita pasarlo a funciones de PHP que aceptan solo un array
o simplemente un Traversable
. Por ejemplo, si quiere hacer un array_diff, y su iterable
es un Traversable
obtendrás un array
. Por el contrario, si llama a una función que toma un iterador, obtendrá un error si el iterable
es un array
.
¿Hay algo como iterable_to_array
(NO: iterator_to_array
) y iterable_to_traversable
?
Estoy buscando una solución que evite los condicionales en mis funciones solo para solucionar esta diferencia, y eso no depende de que yo defina mis propias funciones globales.
Usando PHP 7.1
No estoy seguro de que esto sea lo que está buscando, pero esta es la forma más corta de hacerlo.
$array = [];
array_push ($array, ...$iterable);
No estoy muy seguro de por qué funciona. Acabo de encontrar su pregunta interesante y empiezo a jugar con PHP
Ejemplo completo:
<?php
function some_array(): iterable {
return [1, 2, 3];
}
function some_generator(): iterable {
yield 1;
yield 2;
yield 3;
}
function foo(iterable $iterable) {
$array = [];
array_push ($array, ...$iterable);
var_dump($array);
}
foo(some_array());
foo(some_generator());
Sería bueno si funciona con la función. array()
, pero debido a que es una construcción de lenguaje es un poco especial. Tampoco conserva claves en matrices asoc.
-
Esto funciona porque array_push toma una cantidad variable de argumentos y descomprimes el iterable con … en una lista de argumentos. No es una solución aceptable para mí debido a la necesidad de introducir una variable adicional en el alcance y luego mutarla.
– Jeroen De Dauw
16 de junio de 2017 a las 14:13
-
@JeroenDeDauw Sé que … lo despliega pero en array() no funciona de la misma manera, por lo que hay algo extraño al respecto. Estoy de acuerdo en que no es una solución aceptable, de hecho, creo que no es posible en 7.1 pero es lo más cercano que encontré considerando las limitaciones (no definir funciones y usar condicionales)
– Edwin Rodríguez
16 de junio de 2017 a las 17:47
-
no funcionará para el generador, si el generador no devuelve nada. luego aparece un error: no se pasan argumentos a
array_push
desde...
se resolverá en nada.– Oleg Abrazhev
21 de octubre de 2019 a las 8:08
-
Dado que PHP 7.3 no arrojará ningún error para iterables vacíos usando el código anterior,
array_push
se puede llamar con sólo el primer argumento.– dakujem
8 de marzo de 2021 a las 9:55
Miguel Petri
Para php >= 7.4 esto funciona bastante bien desde el primer momento:
$array = [...$iterable];
Editar: Funciona solo mientras el iterable no contenga claves de cadena
-
Ahora funciona con claves de cadena en PHP 8.1: 3v4l.org/UkdLD
– Ben Morel
8 de noviembre de 2021 a las 11:54
Se puede hacer así:
$array = $iterable instanceof \Traversable ? iterator_to_array($iterable) : (array)$iterable;
¿Hay algo como iterable_to_array e iterable_to_traversable?
Simplemente agréguelos a su proyecto en algún lugar, no ocupan mucho espacio y le brindan las API exactas que solicitó.
function iterable_to_array(iterable $it): array {
if (is_array($it)) return $it;
$ret = [];
array_push($ret, ...$it);
return $ret;
}
function iterable_to_traversable(iterable $it): Traversable {
yield from $it;
}
Nerón
Los términos son fáciles de mezclar
- Traversable
- Iterador (lo veo como un tipo concreto, como la clase A definida por el usuario)
- Iterador Agregado
- iterable (esto es un pseudo-tipo, formación o transitable son aceptados)
- matriz (Este es un tipo concreto, y no es intercambiable con Iterator en el contexto de que se requiere un tipo Iterator)
- arrayIterator (se puede usar para convertir una matriz en un iterador)
Entonces, es por eso que si la función A(iterable $a){}, entonces acepta el parámetro de una matriz o una instancia de transitable (Iterator, IteratorAggregate se aceptan porque es obvio que estas dos clases implementan Traversable. En mi prueba, pasar ArrayIterator también funciona).
En caso de que se especifique el tipo de iterador para el parámetro, pasar una matriz causará TypeError.
TimWolla
A partir de PHP 8.2, el iterator_to_array()
y iterator_count()
las funciones aceptarán iterable
en lugar de Traversable
. Así empezarán a aceptar array
s y haga lo que esperaría que hicieran cuando se encuentren con una matriz.
Específicamente se cumplen las siguientes igualdades:
iterator_to_array($array, true) == $array
iterator_to_array($array, false) == array_values($array)
y
iterator_count($array) == count($array)
Se pueden encontrar más detalles en el RFC correspondiente: PHP RFC: hacer que la familia iterator_*() acepte todos los iterables.
sevavietl
Puedes usar iterator_to_array
convirtiendo tu variable en Traversable
primero:
$array = iterator_to_array((function() use ($iterable) {yield from $iterable;})());
El método de conversión se toma del comentario debajo de esta pregunta.
Aquí está demostración de trabajo.
-
Simplemente puede convertir su propiedad iterable en una matriz
$array = (array) $iterable;
– Fabien Sallés
31 de octubre de 2019 a las 16:17