duduromeroa.com

Programación web

Convertir elementos de arreglo en variables (y viceversa), y recorrer elementos e índices según ubicación de puntero en arreglos de PHP


www.duduromeroa.com, animación, lector, gif

Por Eduardo Romero

Guayaquil, Ecuador



ATENCIÓN: Los conceptos a continuación requieren de conocimiento previo en fundamentos de programación. Reseñas de otros lenguajes de programación se hallan en este vínculo.



Lo revisado en la sección anterior

En la sección anterior se revisaron los arreglos dimensionales y las funciones de alteración de arreglos, como el insertar o eliminar elementos. Ahora seguimos con técnicas para convertir arreglos en variables independientes.

Creando variables desde elementos de arreglo con las funciones extract() y compact()

Toma los elementos de un arreglo asociativo ("clave" : "valor") y los convierte en datos variable = valor. OJO: Cada clave de cada elemento de arreglo asociativo será el nombre de la variable:

En .PHP

<?php 

    $duo = [
      // CLAVE ----------- Valor
        'Bolero' => 'Julio Jaramillo',
        'Requintero' => 'Ney Moreira Moreira'
    ];

    // Obtener claves-valor en forma de arreglo
    extract($duo);

    echo $Bolero."\n"; 
    // Julio Jaramillo

    echo $Requintero;  
    // Ney Moreira Moreira

    echo $amigo;
    // CERO. Esa CLAVE no existe. 

?>

  • Cuidado con la sobreescritura de variables
  • extract() sobreescribe el valor de una variable anterior con el mismo nombre de una clave de elemento de arreglo.

En .PHP

<?php 

// Una variable anterior
$disquera = "Radio Colombia";

// Arreglo con un elemento de igual nombre 
// ...que la variable anterior
$duo = ['disquera' => 'Radio Cristal'];
extract($duo);

// Se sobreescribe
echo $disquera;
//Radio Cristal

?>

  • EVITAR SOBREESCRITURA
  • Usar como segundo parámetro de extract() el operador EXTR_SKIP
  • Y luego acceder (para confirmar cambios) a las dos variables (la global, y la que fue elemento del arreglo).

En .PHP

<?php 

$vocales = "aei";
$arr = ['vocales' => 'ou'];
extract($arr, EXTR_SKIP);

// YA NO sobreescribe
echo $vocales."\n";
// aei

// Acceder directamente a la variable del arreglo
echo $arr['vocales'];
// ou

?>

¿Y qué si, al tener iguales nombres identificadores (en variable global y clave de arreglo), deseamos una coexistencia sin choques entre ambos identificadores? Para eso ayuda agregar como segundo parámetro de extract() al operador EXTR_PREFIX_ALL. Este suma un prefijo string al elemento del arreglo convertido en nueva variable y valor. Ese prefijo se lo indica en el tercer argumento de extract().

En .PHP

<?php 

// Nombre de variable anterior
$cena = "ravioles";

// Arreglo con clave igual a variable global
$hotel = ['cena' => 'mariscos'];

// Se pide:
// A los elementos del arreglo $hotel
// adjuntar prefijo "nueva"
extract($hotel, EXTR_PREFIX_ALL, 'nueva');

// Invoco variable global
echo "Variable anterior:" . $cena."\n";   
// Variable anterior:ravioles

// Invoco variable creado con 'EXTR_PREFIX_ALL'
echo "Variable nueva: " . $nueva_cena;  
// Variable nueva: mariscos

?>

Crear un arreglo asociativo desde variables (y valores) con compact()

Captura variables (identificador + valor) y los organiza en un arreglo asociativo común. No pudiera ser de otra. Los arreglos asociativos contienen elementos con la dupla "nombre" = "valor".

Y, ¿quién da el nombre al arreglo? Pues, la variable que aloja el contenido de compact():

En .PHP

<?php 

// Variable + valor
$ciudad = "Guayaquil";

// Variable + valor
$edad = 45;

// Crear nuevo arreglo
// Aloja compact()
// Está implícito que cada nombre acoge su valors
$persona = compact('ciudad', 'edad');

// Invoco el nombre de la NUEVA variable
print_r($persona);

/*
(
    [nombre] => Carlos
    [edad] => 28
)
*/

?>

Y, para extraer elementos de arreglo y convertirlos en variables y nuevamente crear un arreglo:

En .PHP

<?php 

// Arreglo
$persona = [
    'nombre' => 'Luis',
    'edad' => 35
];

// Lo convierte en variables
// crea $nombre y $edad
extract($persona); 

// A eso lo vuelve a convertir en arreglo
$resumen = compact('nombre', 'edad');

print_r($resumen);

/*
Array ([nombre] => Luis, [edad] => 35)
*/

?>

Recorriendo arreglos en PHP: constructor foreach(){}

foreach()puede recorrer cada elemento de un arreglo, ya sea su clave (ejemplo abajo) o su clave más su valor (ejemplo posterior).

En .PHP

<?php 

// Arreglo
$departamentos = array("ropa", "comida");
// Recorre
foreach ($departamentos as $guardiaRecorre) { 
    echo "- Recorriendo {$guardiaRecorre}\n";
}

/*
- Recorriendo ropa
- Recorriendo comida
*/

?>

En .PHP

<?php 

// Arreglo asociativo
$edades = [
    'Carlos' => 34,
    'Lucía' => 28,
    'Marco' => 41
];

// 
foreach ($edades as $nombre => $enanoQueRecorre) {
    echo "$nombre tiene $enanoQueRecorre años\n";
}

/*
Carlos tiene 34 años
Lucía tiene 28 años
Marco tiene 41 años
*/

// Aún aloja el último valor recorrido
echo $enanoQueRecorre;
// 41

?>

Nótese que el constructor foreach(){} aloja temporalmente cada elemento recorrido en una variable (el último parámetro entre los paréntesis).

Esa variable (que he nombrado en el ejemplo de arriba como $enanoQueRecorre) aloja una copia del elemento del arreglo recorrido por foreach(){}. Y la memoria de esa variable solo alojará el último valor del último elemento recorrido (en el caso de arriba, el valor 41).

Y, si ahora deseamos intercalar cada valor (es decir, de cada variable) iterado y así darle contexto mediante texto. Las interpolaciones de variables mediante el uso de llaves {...} son una buena opción:

En .PHP

<?php 

$guayaquil = array(
    'sur' => "Barrio Esteros", 
    'centro' => "Barrio Garay", 
    'norte' => "Barrio Urdesa");

foreach ($guayaquil as $columnaClave => $columnaValor) { 
    echo "Voy al {$columnaClave} en el {$columnaValor}\n";
}

/*
Voy al sur en el Barrio Esteros
Voy al centro en el Barrio Garay
Voy al norte en el Barrio Urdesa
*/

?>

Discusión: ¿foreach(){} altera o no los valores de los elementos recorridos del arreglo indicado?

Para Tatroe y MacIntyre (2020, p. 240):

  • "foreach(){} no opera sobre el array en sí, sino sobre una copia (de cada elemento) del mismo. Puedes insertar o eliminar elementos en el cuerpo de un bucle foreach(){}, y el bucle no intentará procesar los elementos eliminados o insertados".

Sin embargo, noto ambigua esa afirmación. Si bien foreach(){} no borra ni añade nada nuevo al arreglo original, sí usa una copia de cada valor de elemento para ejecutar lo que la sintaxis del código del usuario indique.

Por otro lado, añadir el operador $ al nombre de la variable que obtiene una copia le indicará al sistema que use una referencia directa de cada elemento y así, alterarlo desde su estado original.

En .PHP

<?php 

// ARREGLO CON ELEMENTO ORIGINAL ALTERADO
$arregloA = [10];

// Usamos el operador & para que $valor 
// sea una referencia al elemento real del array    
foreach ($arregloA as &$valorcito) {

// La referencia se aloja como elemento real del arreglo
$valorcito = $valorcito + 5;
}

// Liberar la referencia después del bucle
// La referencia sigue activa y podría 
// modificar elementos por accidente si se reutiliza.
unset($valorcito);

// Mostrar
print_r($arregloA);
/*
([0] => 15)
*/

/* ¿SE DESEA NO ALTERAR LOS VALORES? */
// Simplemente eliminar el operador &. 
// Si eliminas el operador &, 
// el foreach() trabajará con copias, 
// y no alterará el array original.
?>

Finalmente, foreach(){} crea una lista fantasma y temporal de índices y claves del arreglo original. Por lo que recorrerlos mediante alguna alteración de esos elementos no interrumpirá el recorrido.

Funciones de iterador

Llamamos iterador a un invisible puntero que indica un valor de elemento de arreglo. Si bien no lo vemos, la sintaxis da por sentado que ese puntero señala un valor de elemento o, por el contrario, se moverá para señalar algún otro elemento a lo largo del arreglo.

El objetivo de las funciones de iterador es indicar "a dónde apuntar" y según eso, operar ese valor apuntado. Esas funcione no alteran a los elementos de un arreglo.

En .PHP

<?php 

// EJEMPLO CONCEPTO
-------- *
Índice: [0]  [1]  [2]  [3]  [4]
$Letras ["A", "B", "C", "D", "E"];
// El asterisco sería el iterador invisible. 
// Se posa en el primer elemento (índice cero)s

?>

Basado en el ejemplo-concepto de arriba, las funciones de iterador cumplen con lo siguiente:

  • current($letras) → Devuelve "A" (el valor bajo el asterisco).
  • next($letras) → Mueve el asterisco una posición a la derecha, sobre "B".
  • end($letras) → Lo lleva al final: sobre "E".
  • reset($letras) → Lo regresa al principio: sobre "A".
  • prev($letras) → Lo mueve una posición hacia la izquierda (si puede).
  • key($letras) → Devuelve el índice donde está el asterisco.
  • each($letras) → Devuelve ['key' => ..., 'value' => ...] del elemento actual y luego avanza el asterisco.

¿Por qué necesitamos una función de iterador si ya podemos acceder a cualquier elemento del arreglo mediante la ubicación del índice?

Es cierto que una vez establecidos los elementos de un arreglo podemos acceder a sus valores mediante sintaxis de índice:

En .PHP

<?php 

$elementosUnicos = ["A", "B", "C"];
$elementosValores = ["A" => 1, "B" => 2];

echo $elementosUnicos[0]."\n";
// A

echo $elementosValores["A"];
// 1

?>

Sin embargo, las funciones de iterador en PHP pueden ser útiles en los siguientes casos:

  • Cuando el arreglo no tiene índices numéricos o inicie con algún otro número o conun string

    En .PHP

    <?php 
    
    $arreglo = [100 => "uno", 200 => "dos"];
    echo $arreglo[0]; 
    // Error: no existe
    
    ?>
    

  • Cuando la clave del último elemento de un arreglo asociativo extenso sea desconocido

    En .PHP

    <?php 
    
    // Mostrar el último valor del último elemento 
    // (no sabemos cuántos hay)
    echo $arreglo[count($arreglo) - 1];
    // Solo funciona si no es asociativo
    
    ?>
    

Función iterador current()

Esta función captura la ubicación actual del puntero interno del arreglo. El elemento actual es una ubicación dinámica: está en el índice cero cuando el arreglo ha sido creado, o está en cualquier otro índice si así lo indicamos mediante sintaxis de funciones de iterador.

En .PHP

<?php 

$datos = ["uno", "dos", "tres"];

// Al inicio:
echo current($datos)."\n"; 
// uno -> ES el elemento actual

// Puntero va al siguiente índice
next($datos)."\n";
echo current($datos)."\n"; 
// dos -> ahora el puntero se movió, ese es el nuevo elemento actual

// Puntero va al final 
end($datos)."\n";
echo current($datos)."\n"; 
// tres -> el puntero ahora está al final

?>

Función iterador next()

El puntero se mueve adelante de su estado anterior. Si el puntero está al final, este "se cae" y al no captar nada, envia false. Si el puntero ya está en una ubicación intermedia cuando inicia un bucle, el puntero iniciará (y devolverá) ese valor del elemento y seguirá con el recorrido de los elementos restantes.

En .PHP

<?php 

$datos = ["uno", "dos", "tres"];

// Aqui el puntero está en elemento "dos"
echo next($datos)."\n"; 
// dos

// Como está en "dos" entonces inicia desde allí
while ($recorre = current($datos)) {
    echo $recorre . "\n";
    next($datos);
}

/*
    dos
    tres
*/

// Si se necesita que el puntero inicie desde el inicio
reset($datos);

?>

Función iterador end()

Mueve y captura el puntero del último índice del último elemento del arreglo. Aunque la cantidad de elementos sea indefinida, end() siempre captura el elemento último. Si el arreglo está vacío da false.

En .PHP

<?php 

$letras = ['A', 'B', 'C'];
echo end($letras)."\n"; 
// C

$persona = [
    'nombre' => 'José Joaquín',
    'ciudad' => 'Guayaquil'
];

echo end($persona); 
// Guayaquil

?>

Función iterador reset()

Mueve y captura el valor del primer elemento. Nada más que decir allí. Incluso si la ubicación del puntero fue indefinida –con next() o end()–. Es un "retorno a casa (al índice cero)" absoluto. Si es un arreglo vacío da false.

Función iterador prev()

Mueve y captura el valor del elemento anterior a la posición actual del puntero. Es decir, si estaba al final, apuntará al penúltimo. Nada más que decir aquí.

En .PHP

<?php 

$colores = ['rojo', 'verde', 'azul'];

echo end($colores)."\n";         
// puntero en "azul"

echo prev($colores)."\n";         
// verde

echo prev($colores)."\n";         
// rojo

echo current($colores)."\n";         
// rojo (posición actual)

?>

Función iterador key()

Capta la CLAVE (el identificador de un valor de elemento de arreglo) del elemento en donde el puntero está actualmente. Si es un índice numérico, da un entero; si es un índice asociativo, da el nombre de la clave. Nada más que decir aquí.

En .PHP

<?php 

$persona = [
    'nombre' => 'Julio Jaramillo',
    'profesion' => "cantante"
];

// Se ubica al final
end($persona); 
// da LA CLAVE de ese elemento final

echo key($persona); 
// profesion

?>

Función iterador each()

Es una función eliminada de la sintaxis moderna de PHP. Devuelve la dupla "clave:valor" de la ubicación actual del puntero. Luego, ese puntero avanzará un puesto adelante (similar a un next()). . Su mejor reemplazo es la función each().

En la siguiente sección revisaremos una interesante dinámica: la de activar funciones desde un elemento de arreglo en PHP.