USO DE MATRICES
Transcripción
USO DE MATRICES
USO DE MATRICES En el tercer artículo de esta serie vimos una introducción al uso de matrices en PHP y sentamos las bases de su uso. Ahora vamos a descubrir toda la potencia que este lenguaje tiene para manejar estas colecciones de datos de forma eficiente, facilitando nuestro trabajo como desarrolladores. LAS MATRICES EN PHP PHP, a pesar de ser un lenguaje de script, tiene una flexibilidad y una potencia realmente increíbles. Quizá sea esa misma flexibilidad uno de sus puntos débiles, en lo que respecta a ascpectos como el tipaje de datos, al menos en opinión de los más puristas. Nosotros no vamos a entrar a polemizar sobre ese punto (no es el objetivo de estos artículos). En lugar de ello, vamos a centrarnos en conocer uno de los aspectos más versátiles y funcionales de PHP: las matrices. Ya sabemos que las matrices son colecciones de datos que se almacenan en memoria bajo un único nombre, identificándose individualmente mediante un índice o una clave asociativa. Una matriz se puede declarar sin contenido inicial, como vemos a continuación: $matriz = array(); Las matrices son un elemento que presenta diferencias en sus características y operativa, según con que lenguaje estemos trabajando. En PHP, las matrices tienen las siguientes características: • Al declararse se puede establecer el número de elementos que tendrá, o no establecerse. En realidad, las matrices en PHP se redimensionan dinámicamente, de forma que siempre podremos agregar o eliminar elementos de la misma. • Las claves pueden ser un índice numérico o una clave asociativa. En una matriz pueden darse elementos cuya clave sea indexada y otros con clave asociativa. Esto es así porque, en PHP, todas las matrices son, subyacentemente, asociativas. El intérprete trata los índices como claves asociativas, de forma transparente al programador. • Una matriz puede tener una, dos, o más dimensiones. Puede tener todas las dimensiones que se necesiten. Sin embargo, rara vez se necesitan matrices de tres dimensiones y yo, personalmente, en más de diez años escribiendo código, nunca he necesitado más de tres. • En una matriz puede haber elementos cuyos valores sean de diferentes tipos (cadenas, numéricos, booleanos, etc). Otros lenguajes, como Java, no permiten tanta flexibilidad. • Existen gran cantidad de formas de ordenar matrices (por clave o por valor), combinarlas, recuperar elementos, etc. CREANDO MATRICES En PHP se puede crear una matriz vacía, como hemos visto arriba. También podemos crear una matriz asignado unos valores iniciales, así: $nombres = array("Pepe", "Laura", "Lucía", "Alfonso", "Pedro", "Manuela"); Esto daría como resultado la matriz que vemos a en la tabla adjunta. Como, al crearla, no hemos asignado claves (sólo valores), el intérprete genera claves indexadas, consecutivas, empezando desde 0. Tabla de nombres Podríamos haber creado la matriz con los índices que deseáramos, así: $nombres = array(1=>"Pepe", 2=>"Laura", 5=>"Lucía", 6=>"Alfonso", 7=>"Pedro", 10=>"Manuela"); Esto dará una matriz como la que aparece a la derecha. Como ves, podemos fijar los índices como deseemos. El iniciar desde cero y obtener índices consecutivos es sólo el comportamiento por defecto en la creación de matrices, pero puede ser alterado. En general, debes tener en cuenta que, a la hora de crear una matriz: Matriz con los índices establecidos • Por defecto, el índice empieza en cero, y los subsiguientes se crean de forma consecutiva. • Si se establece un índice, y no se establece otro subsiguiente, este último se numerara a partir del último. Por ejemplo, supongamos la siguiente declaración: $nombres = array(1=>"Pepe", 2=>"Laura", 5=>"Lucía", "Alfonso", "Pedro", "Manuela"); En este caso, Alfonso tendría el índice 6, Pedro el 7 y Manuela el 8. • Si los índices se establecen de tal modo que no son consecutivos, el intérprete NO reserva espacio en memoria para los que no han sido creados, de modo que la matriz no gasta recursos de máquina que no son necesarios. Podemos crear claves asociativas, usando una declaración como la siguiente: $datos = array("nombre"=>"Pedro", "apellidos" =>"García de la Torre", "Ciudad"=>"París", "edad"=>29); Podemos crear una matriz, como hemos dicho, con más de una dimensión, como una tabla con filas y columnas. En realidad, es como una matriz de matrices. Suponga la siguiente declaración: $datosDeAmigos = array("Amigo1"=>array("nombre"=>"Pedro", "apellidos" =>"García de la Torre", "Ciudad"=>"París", "edad"=>29), ("Amigo2"=>array("nombre"=>"Luis", "apellidos" =>"Pérez Gómez", "Ciudad"=>"Toledo", "edad"=>32), ("Amigo3"=>array("nombre"=>"Antonio", "apellidos" =>"Blanco Antúnez", "Ciudad"=>"Madrid", "edad"=>18), ("Amigo4"=>array("nombre"=>"Carmen", "apellidos" =>"Iglesias Flores", "Ciudad"=>"Barcelona", "edad"=>23)); Como ves, se trata de una matriz asociativa (con las claves "Amigo1", "Amigo2", "Amigo3" y "Amigo4"). Cada uno de los elementos es, a su vez, otra matriz asociativa con los datos de un amigo. Por cierto, observa que algunos datos son alfanuméricos y otros (la edad) son numéricos. RECUPERANDO DATOS Recuperar los datos de una posición concreta de la matriz cuando es necesario es tan importante como introducirlos. Para acceder al valor de un elemento, lo referenciamos usando las claves, colocadas entre corchetes. Si la clave es asociativa, deberemos meterla entre comillas. Por ejemplo, si queremos recuperar la ciudad del segundo amigo de la última matriz que hemos creado, lo haremos así: $ciudadBuscada = $datosDeAmigos["Amigo2"]["ciudad"]; En el caso de una matriz indexada podemos referenciar un elemento de la misma forma: $elemento = $matriz[45][12]; Por supuesto, tanto si la matriz es asociativa, como si es indexada, o si tiene una dimensión o dos, el índice puede ir expresado como una variable, así: $elemento = $matriz[$indice]; Por lo tanto, podemos recuperar todos los elementos de una matriz usando los bucles que ya hemos visto en el artículo 4 de esta serie, como vemos a continuación: <?php $ciudades = array("París","Londres","Bucarest","Madrid","Roma"); for ($i = 0; $i < count($ciudades); $i++){ echo $ciudades[$i]."<br />"; } ?> Sin embargo, hay algo que debemos tener en cuenta. Si a la matriz se le han asignado índices específicos, este sistema no nos vale. Observa el listado errorEnFor.php. <?php $ciudades = array(2=>"París", 7=>"Londres", 10=>"Bucarest", 14=>"Madrid", 3=>"Roma"); for ($i = 0; $i < count($ciudades); $i++){ echo $ciudades[$i]."<br />"; } ?> El problema que aparece es que este código usa la función count() para determinar cuántos elementos tiene la matriz... y asume que estos están numerados consecutivamente a partir del 0, tal como se establece en la definición del bucle. Sin embargo, al haber especificado otros índices en la creación de la matriz, algunos elementos no existen. Por ejemplo, cuando, en el cuerpo del bucle, se hace referencia a $ciudades[0], nos encontramos con que es un elemento no definido. Como hemos dicho antes, PHP no reserva espacio en memoria para los elementos no definidos, así que nos da un error, avisándonos de que el índice (offset) no está definido en la matriz, tal como vemos en la ilustración 1. En realidad, si queremos recorrer todos los elementos de Ilustración 1 una matriz en PHP, un bucle de tipo for no es la solución más adecuada. En su lugar debemos emplear los bucles de tipo foreach que vimos en el artículo 4 de esta serie, que son perfectos para este tipo de tareas. ATENCIÓN. Para que se muestren los errores que se producen en tiempo de ejecución es necesario que esté activada la directiva display errors. En un artículo posterior hablaremos sobre las directivas de configuración de PHP. En el ejercicio que acabamos de ver se podrían haber evitado los avisos de error mediante el uso de la función array_key_exists(), que determina si un índice concreto existe o no en la matriz. Mira el código parcheParaFor.php: <?php $ciudades = array(2=>"París", 7=>"Londres", 10=>"Bucarest", 14=>"Madrid", 3=>"Roma"); for ($i = 0; $i < count($ciudades); $i++){ if array_key_exists($i, $ciudades) echo $ciudades[$i]."<br />"; } ?> La función array_key_exists() recibe dos argumentos. El primero es el índice que vamos a buscar en la matriz y el segundo es la matriz en la que buscamos ese índice. Devuelve un valor booleano, indicando si dicho índice existe o no en la matriz especificada. De este modo, si no existe un índice, el programa no intenta procesarlo, y no da un error. Por supuesto, esto no resuelve el problema de que, con este bucle en esta matriz no se recuperan todos los elementos. Insisto. Para recorrer matrices, mejor usa un bucle foreach. Hasta ahora, hemos visto como recuperamos elementos de una matriz accediendo a su contenido, sin modificar el contenido de la matriz. Si queremos, una vez localizado un elemento, eliminarlo de la matriz, debemos hacerlo en dos pasos, así: $ciudad = $ciudades[0]; unset ($ciudades[0]); Lo vemos en el código verYEliminar.php: <?php $ciudades = array("París", "Londres", "Bucarest", "Madrid", "Roma"); echo "La matriz de ciudades tiene los siguientes elementos:"; var_dump ($ciudades); echo "La primera ciudad es ".$ciudades[0]; unset ($ciudades[0]); echo "Ahora la matriz contiene las siguientes ciudades:"; var_dump ($ciudades); ?> El resultado de la ejecución aparece en la ilustración 2. Como ves, en primer lugar mostramos la matriz completa. Después, tras recuperar, y eliminar expresamente, uno de los elementos, se muestra la matriz con los restantes. Además, puedes apreciar que a los demás elementos no se les han modificado sus índices tras eliminar el primero. Podemos extraer un elemento, eliminándolo de la matriz, en un solo paso. Ilustración 2 Para ello recurrimos a la función array_shift(), que actúa sobre el primer elemento, o a array_pop(), que actúa sobre el último. Puedes ver cómo usar estas funciones en los códigos extraerPrimero.php y extraerUltimo.php: <?php // eliminarPrimero.php $ciudades = array("París", "Londres", "Bucarest", "Madrid", "Roma"); echo "La matriz de ciudades tiene los siguientes elementos:"; var_dump ($ciudades); echo "La primera ciudad es ".array_shift($ciudades); echo "Ahora la matriz contiene las siguientes ciudades:"; var_dump ($ciudades); ?> <?php // eliminarUltimo.php $ciudades = array("París", "Londres", "Bucarest", "Madrid", "Roma"); echo "La matriz de ciudades tiene los siguientes elementos:"; var_dump ($ciudades); echo "La primera ciudad es ".array_pop($ciudades); echo "Ahora la matriz contiene las siguientes ciudades:"; var_dump ($ciudades); ?> Antes de entrar en comentarios sobre estas funciones, observa el resultado de la ejecución de las mismas en las ilustraciones 3 y 4. Ilustración 3 Ilustración 4 Como ves, lo que hacen estas funciones es extraer el primero o el último elemento (a una variable, o dispositivo de salida, etc.) eliminándolo de la matriz. En el caso de array_shift(), al extraer el primer elemento, se recrean los índices de la matriz. Esto es importante, porque los valores que permanecen en la misma ya no tienen los mismos índices que antes. Observa el listado eliminarPrimeroDesordenados.php y vea su resultado en la ilustración 5. Con la extracción del último elemento ocurre lo contrario: los restantes índices no se alteran. Tienes el código en eliminarUltimoDesordenados.php, y el resultado en la ilustración 6. <?php // eliminarPrimeroDesordenados.php $ciudades = array(3=>"París", 5=>"Londres", "Bucarest", 12=>"Madrid", "Roma"); echo "La matriz de ciudades tiene los siguientes elementos:"; var_dump ($ciudades); echo "La primera ciudad es ".array_shift($ciudades); echo "Ahora la matriz contiene las siguientes ciudades:"; var_dump ($ciudades); ?> <?php // eliminarUltimoDesordenados.php $ciudades = array(3=>"París", 5=>"Londres", "Bucarest", 12=>"Madrid", "Roma"); echo "La matriz de ciudades tiene los siguientes elementos:"; var_dump ($ciudades); echo "La primera ciudad es ".array_pop($ciudades); echo "Ahora la matriz contiene las siguientes ciudades:"; var_dump ($ciudades); ?> Ilustración 5 Ilustración 6 Hemos visto como extraer el primer elemento de la matriz y el último. Pero, ¿qué pasa si queremos recuperar elementos de otra parte de la matriz? La función array_slice() nos permite, exactamente, eso. Observa el listado de usoDeSlice_01.php: <?php // usoDeSlice_01.php $ciudades = array(3=>"París", 5=>"Londres", "Bucarest", 12=>"Madrid", "Roma"); echo "La matriz de ciudades tiene los siguientes elementos:"; var_dump ($ciudades); $matrizSecundaria = array_slice($ciudades, 2); echo "La matriz secundaria contiene los siguientes elementos:"; var_dump ($matrizSecundaria); echo "Ahora la matriz contiene las siguientes ciudades:"; var_dump ($ciudades); ?> La función array_slice() recibe dos argumentos obligatorios: el nombre de la matriz de la que se van a recuperar los datos y el número de elemento a partir del que se van a recuperar. Es importante que entiendas que, en este contexto, "número de elemento" no tiene nada que ver con la clave (sea indexada o asociativa) que tiene cada elemento. A efectos de esta función, los elementos de la matriz se empiezan a contar desde 0. Por tanto, el elemento 2 de la matriz es el tercero que exista, sean cuales sean las claves que tengan los distintos elementos. Esto es así por lo que decíamos anteriormente: el intérprete maneja, internamente, todas las matrices como asociativas. La función puede, además, recibir otros dos argumentos opcionales. Con la sintaxis que hemos visto en este primer ejemplo, se recuperan los elementos desde el especificado hasta el final de la matriz. Así pues, este script recupera los elementos desde el 2 (es decir, el tercero de la matriz) hasta el final. Lo puedes ver en la ilustración 7. Observa, también, que esta función no extrae nada de la matriz, no la modifica en modo alguno. La matriz original sigue conservando todos sus elementos. Los que la función selecciona se copian en otra matriz. Ilustración 7 El siguiente argumento (opcional) que puede recibir esta función es para limitar los elementos que se copian, de modo que no se copie "hasta el final de la matriz". Esto nos permite copiar una parte de la matriz, acotada con un primero y un último elemento. Observa el listado usoDeSlice_02.php: Ilustración 8 <?php // usoDeSlice_02.php $ciudades = array(3=>"París", 5=>"Londres", "Bucarest", 12=>"Madrid", "Roma"); echo "La matriz de ciudades tiene los siguientes elementos:"; var_dump ($ciudades); $matrizSecundaria = array_slice($ciudades, 2, 1); echo "La matriz secundaria contiene los siguientes elementos:"; var_dump ($matrizSecundaria); ?> En la ilustración 8 puedes ver que se recupera un elemento (como especifica el tercer argumento) a partir del elemento 2 de la matriz (lo especificado en el segundo argumento). Observa, también, que al copiar elementos a la matriz secundaria, se pierden las claves que dichos elementos tenían originalmente. Es decir, se copian sólo los valores, pero no las claves. Esto cambia si recurrimos al cuarto argumento de la función. Es un valor booleano que indica si se deben mantener las claves. Por defecto es false, pero se puede poner en true. Ilustración 9 Observa el listado usoDeSlice_03.php: <?php // usoDeSlice_03.php $ciudades = array(3=>"París", 5=>"Londres", "Bucarest", 12=>"Madrid", "Roma"); echo "La matriz de ciudades tiene los siguientes elementos:"; var_dump ($ciudades); $matrizSecundaria = array_slice($ciudades, 2, 1, true); echo "La matriz secundaria contiene los siguientes elementos:"; var_dump ($matrizSecundaria); ?> En la ilustración 9 ves el resultado, donde se ha copiado todo el elemento (valor y clave). Al segundo y tercer argumento, se les pueden dar valores negativos, lo que modifica su comportamiento. Observa el listado usoDeSlice_04.php: <?php // usoDeSlice_04.php $ciudades = array(3=>"París", 5=>"Londres", "Bucarest", 12=>"Madrid", "Roma"); echo "La matriz de ciudades tiene los siguientes elementos:"; var_dump ($ciudades); $matrizSecundaria = array_slice($ciudades, -2, 2, true); echo "La matriz secundaria contiene los siguientes elementos:"; var_dump ($matrizSecundaria); ?> Como ves, hemos puesto en negativo el segundo argumento. Esto introduce cambios en su comportamiento: • En primer lugar, significa que los elementos empiezan a contarse a partir del final de la matriz. • También hace que la cuenta empiece en 1 y no en 0. Así, en nuestro ejemplo, se empiezan a Ilustración 10 recuperar elementos a partir del segundo, comenzando por el final de la matriz. El resultado lo ves en la ilustración 10. Y ¿qué pasa si el tercer argumento es negativo? Mira el listado de usoDeSlice_05.php: <?php // usoDeSlice_05.php $ciudades = array(3=>"París", 5=>"Londres", "Bucarest", 12=>"Madrid", "Roma"); echo "La matriz de ciudades tiene los siguientes elementos:"; var_dump ($ciudades); $matrizSecundaria = array_slice($ciudades, 2, -2, true); echo "La matriz secundaria contiene los siguientes elementos:"; var_dump ($matrizSecundaria); ?> Ilustración 11 Presta atención, porque este es un concepto más difícil de familiarizarse con él. El tercer argumento define, como hemos visto, el tamaño (número de elementos) de la recuperación. Si es negativo, determina a cuantos elementos del final de la matriz se detiene la recuperación. En nuestro ejemplo, la recuperación se detiene cuando falten dos elementos para acabar la matriz. Lo verás más claro si analizas el código y lo comparas con el resultado que aparece en la ilustración 11. Como puedes ver, la recuperación se detiene cuando faltan dos elementos para acabar la matriz. LOCALIZANDO ELEMENTOS A menudo no es tan necesario extraer un elemento de una matriz como localizar si existe o no. Suponte que debes comprobar si un elemento está en la matriz o no, de modo que, en función del resultado, tu script de PHP deba hacer una cosa u otra. Esta necesidad es mucho más común de lo que piensas. Cuando tienes matrices complejas, con una gran cantidad de datos, es muy fácil que tu problema sea detectar si un elemento está en la matriz. Por supuesto, podrías hacerlo "a piñón", recorriendo la matriz en un bucle, así: $existePepe = false; foreach ($matrizNombres as $clave=>$nombre){ if ($nombre == "Pepe"){ $existePepe = true; break; } } if ($existePepe){ echo "Pepe existe"; } else { echo "Pepe no existe"; } En otros lenguajes, esta sería la única forma de detectar la existencia de un elemento. En PHP, sin embargo, esto no es más que una manera chapucera de perder el tiempo. El lenguaje nos proporciona mejores recursos para localizar la existencia de un elemento. Si lo que queremos es saber si un valor existe en la matriz, usaremos la función in_array(), que nos devuelve un valor lógico, indicándonos si un elemento concreto figura, como valor, en la matriz. Si queremos saber si dicho elemento existe como clave, usaremos la función array_key_exists(). Ambas funciones reciben dos argumentos. El primero es "lo que buscamos" y el segundo es la matriz donde lo buscamos. Observa el script busqueda_simple.php: <?php $matrizDeNombres = array("Pedro", "Alfonso", "Carmen", "Laura", "Julio", "Diego", "Lucía", "Ana"); if (in_array("Laura", $matrizDeNombres)){ echo "El elemento Laura existe en la matriz.<br />"; } else { echo "El elemento Laura NO existe en la matriz.<br />"; } if (array_key_exists(3, $matrizDeNombres)){ echo "La clave 3 existe en la matriz.<br />"; } else { echo "La clave 3 NO existe en la matriz.<br />"; } ?> Pruébalo y veras como operan estas funciones. El problema aparece cuando se intenta localizar un contenido en una matriz de dos dimensiones. Suponte una matriz de amigos definida como la que vimos en el artículo tercero de esta serie, así: $amigos = array (array("nombre"=>"Pedro Torres", "direccion"=>"CL Mayor, 37", "telefono"=>"123456789"), array("nombre"=>"Carlos Gómez", "direccion"=>"CL Alfareros, 12", "telefono"=>"567891234"), array("nombre"=>"Susana Casas", "direccion"=>"CL Sierra Grande, 2", "telefono"=>"987654321"), array("nombre"=>"Carmen Pérez", "direccion"=>"CL Himalaya, 189", "telefono"=>"502983948")); Aquí parece que podríamos intentar localizar la existencia de un nombre (por ejemplo), de la siguiente forma: in_array("Susana Casas", $amigos) Sin embargo esto no funciona. Como resulta que en PHP una matriz bidimensional es, realmente, una matriz de matrices, tendremos que buscar el nombre dentro de cada una de las matrices, hasta que lo encontremos. Vemos el código en localizarEnDosDimensiones.php: <?php $amigos = array (array("nombre"=>"Pedro Torres", "direccion"=>"CL Mayor, 37", "telefono"=>"123456789"), array("nombre"=>"Carlos Gómez", "direccion"=>"CL Alfareros, 12", "telefono"=>"567891234"), array("nombre"=>"Susana Casas", "direccion"=>"CL Sierra Grande, 2", "telefono"=>"987654321"), array("nombre"=>"Carmen Pérez", "direccion"=>"CL Himalaya, 189", "telefono"=>"502983948")); $existe = false; foreach ($amigos as $amigo){ if (in_array("Susana Casas", $amigo)){ $existe = true; break; } } echo ($existe)?"Susana Casas existe":"Susana Casas NO existe"; ?> Pruébalo para ver cómo funciona. En ocasiones nos interesa conservar los elementos de una matriz, eliminando las claves que pudiera tener, para evitar que nos induzcan a confusión en otro proceso. Esto lo hacemos con la función array_values(), que recibe como argumento una matriz y nos genera otra, con las claves convertidas a indexadas y empezando desde 0. Observa el listado listaDeValores.php: Ilustración 12 <?php $amigo = array ("nombre"=>"Pedro Torres", "direccion"=>"CL Mayor, 37", "telefono"=>"123456789"); $valores = array_values($amigo); var_dump ($valores); ?> En la ilustración 12 ves el resultado. AGREGAR ELEMENTOS A UNA MATRIZ Esta es una funcionalidad muy flexible en PHP. En otros lenguajes las matrices son colecciones muy rígidas y, una vez creadas, añadir nuevos elementos es "tarea de chinos". En PHP basta con hacer algo así: $matriz[] = "Nuevo elemento"; Así de simple. Si ponemos los corchetes "vacíos" (sin especificar ninguna clave), el nuevo elemento se insertará en la primera posición que esté libre en la matriz, al final de la misma. Observa el listado nuevoElemento.php: <?php $nombres = array (0=>"Javier", 3=>"Manuel", "Raul", "Pedro", "Arturo", "Alfonso"); echo "MATRIZ ORIGINAL<br />"; var_dump ($nombres); $nombres[] = "Lidia"; $nombres[] = "Sonia"; $nombres[] = "María"; echo "MATRIZ CON NUEVOS Ilustración 13 ELEMENTOS<br />"; var_dump ($nombres); ?> Observa el resultado en la ilustración 13, en la que se aprecia como los nuevos elementos se han añadido al final de la matriz. Esta es una construcción muy potente que, a la hora de resolver ciertos procesos, da muchísimo juego. Si lo que queremos es introducir un elemento al final de la matriz también podemos usar la función array_push(). Esta recibe dos argumentos: el primero es el nombre de la matriz en la que queremos introducir otro elemento y el segundo es, naturalmente, el elemento que queremos introducir. Por ejemplo, podríamos ver un código como el siguiente: $nombres = array (0=>"Javier", 3=>"Manuel", "Raul", "Pedro", "Arturo", "Alfonso"); array_push ($nombre, "Lucas"); Pero ¿qué pasa si la matriz tiene más de una dimensión? Ningún problema. Mira el siguiente fragmento: $amigos = array(array("nombre"=>"Rafael", "ciudad"=>"Madrid"), array("nombre"=>"Lorena", "ciudad"=>"Málaga"), array("nombre"=>"Julia", "ciudad"=>"Lugo")); $nuevo_amigo = array("nombre"=>"Ismael", "ciudad"=>"Barcelona"); array_push ($amigos, $nuevo_amigo); Como alternativa, podemos usar la función array_unshift(). Esta actúa de forma similar a la anterior, con la diferencia de que, en este caso, el nuevo elemento se introduce al principio de la matriz, desplazando los demás "hacia abajo". Podemos crear una matriz a partir de una cadena de texto, siempre que respetemos el hecho de que los valores deben aparecer separados por una secuencia identificable. Para ello contamos con la función explode(). Esta recibe dos argumentos: el delimitador (la secuencia identificable) y la cadena. Observa el siguiente fragmento: $lista_de_nombres = "Luis, Alfonso, Carla, Laura, Rosa, Rafael"; $matriz_de_nombres = explode(", ", $lista_de_nombres); Esto creará una matriz de nombres con los seis nombres que aparecen en la cadena. La función puede recibir un tercer argumento opcional que limite el número de elementos a crear en la matriz. Si hay más elementos en la cadena, los que "sobren" irán en el último elemento de la matriz. Observa el siguiente fragmento: $lista_de_nombres = "Luis, Alfonso, Carla, Laura, Rosa, Rafael"; $matriz_de_nombres = explode(", ", $lista_de_nombres, 3); Esto dará, como resultado, la siguiente matriz: $matriz_de_nombres[0] = "Luis"; $matriz_de_nombres[1] = "Alfonso"; $matriz_de_nombres[2] = "Carla, Laura, Rosa, Rafael"; La función implode() hace justo lo contrario: une todos los elementos de una matriz en una cadena. Observa el siguiente fragmento: $matriz_de_nombres = array(); $matriz_de_nombres[0] = "Luis"; $matriz_de_nombres[1] = "Alfonso"; $matriz_de_nombres[2] = "Carla "; $matriz_de_nombres[3] = "Laura"; $matriz_de_nombres[4] = "Rosa"; $matriz_de_nombres[5] = "Rafael "; $lista_de_nombres = implode(", ", $matriz_de_nombres); // $lista_de_nombres contiene "Luis, Alfonso, Carla, Laura, Rosa, Rafael " Nota. Por razones históricas, implode() acepta los argumentos en cualquier orden. Sin embargo, para evitar confusiones es aconsejable teclearlos así, en el mismo orden que en el caso de explode(). RECORRIENDO UNA MATRIZ En ocasiones es necesario recorrer una matriz de principio a fin, iterando sobre todos sus elementos. PHP nos proporciona una herramienta que nos permite hacer este tipo de operaciones. Se trata de un puntero que señala a un elemento, y puede desplazarse hacia adelante y hacia atrás. Para manejarlo adecuadamente tenemos varias funciones que nos ayudan. La primera que vamos a conocer es current(), que nos indica a que elemento de la matriz está "mirando" el puntero en un momento dado. Recibe como único argumento el nombre de la matriz que nos interesa y devuelve el valor del elemento "actual". Si el puntero está "mirando" más allá del último elemento de la matriz, la función devuelve un false booleano. Esta función no mueve el puntero. La función next() recibe el nombre de la matriz y mueve el puntero al siguiente elemento y nos devuelve su valor. Si el puntero ha quedado "fuera" de la matriz, devuelve false. La función prev() actúa de modo similar, pero moviendo el puntero al elemento anterior. Si el puntero se sitúa "antes" del primer elemento, la función devuelve un false booleano. La función end() recibe, como argumento, el nombre de la matriz y mueve el puntero al último elemento de la misma, devolviendo su valor. Como contrapartida tenemos la función reset(), que sitúa el puntero al principio de la matriz y devuelve el valor del primer elemento. Si la matriz está vacía, devuelve un false booleano. La función each() devuelve el par clave/valor actual para la matriz y avanza el puntero de la misma. Esta pareja se devuele en una matriz de 4 elementos, con las claves 0, 1, key, y value. Los elementos 0 y key contienen el nombre de clave del elemento de la matriz, y 1 y value contienen los datos. Si el puntero interno para la matriz apunta después del final del contenido de la matriz, esta función devuelve un false booleano. Veamos un ejemplo en el listado recorre_matriz.php, reproducido a continuación: <?php Ilustración 14 $matrizDeNombres = array("Pedro", "Luis", "Carmen", "Alfonso", "Roxana", "Carla", "Ramiro"); $elementos = true; while ($elementos){ $elementos = each($matrizDeNombres); echo $elementos["key"]." ".$elementos["value"]."<br />"; } ?> En la ilustración 14 vemos el resultado del script que recorre la matriz de elemento en elemento. ORDENADO UNA MATRIZ En ocasiones no nos vale contar con una matriz "tal como viene", sino que se hace necesario ordenar sus elementos (pares clave - valor), siguiendo unos criterios impuestos por las necesidades de programación. PHP nos proporciona una gran variedad de funciones para ordenar matrices. La función más simple para ordenar una matriz es sort(). Permite ordenar una matriz por el valor de sus elementos. El problema con esta función es que destruye las relaciones entre valores e índices (de hecho, destruye las claves y asigna nuevos índices a los elementos), lo que la hace inoperativa para muchas necesidades. No obstante. está bien conocerla, para aquellos casos en que la ordenación necesaria sea muy elemental, y las claves no sean importantes. Recibe, como argumento, el nombre de la matriz que quieres ordenar. Observa el listado ordenacion_sort.php. <?php $matriz = array("Nombre" => "Pedro", "Ciudad"=>"Albacete", "Profesion"=>"Sexador de pollos"); var_dump($matriz); sort($matriz); var_dump($matriz); ?> En la ilustración 15 ves el resultado, en el que se muestra la matriz antes y después de aplicarle la función. Como ves, deja bastante que desear. Esta función admite un segundo parámetro, opcional, para modificar su comportamiento. Este puede ser una de las siguientes cuatro constantes: Ilustración 15 • SORT_REGULAR. Es el valor que asume por defecto. efectuando la ordenación que vemos en el ejemplo. • SORT_NUMERIC. Efectúa una comparación numérica. Adecuada para valores de este tipo. • SORT_STRING. Efectúa una comparación por cadenas.. • SORT_LOCALE_STRING. Igual que el anterior, pero dependiendo de la ubicación local, que se establece con setlocale() -puedes ver esta función en http://es1.php.net/manual/es/function.setlocale.php, aunque hablaremos de ella más adelante. La función asort() supone una mejora importante en la ordenación de matrices. Mira el listado ordenacion_asort.php: <?php $matriz = array("Nombre" => "Pedro", "Ciudad"=>"Albacete", "Profesion"=>"Sexador de pollos"); var_dump($matriz); asort($matriz); var_dump($matriz); ?> Como ves en la ilustración 16, esta vez se mantienen los índices y su correlación con los valores de los elementos. Esto, que puede parecer importante en matrices indexadas (y que, habitualmente, lo es), reviste una especial trascendencia en matrices asociativas. Ilustración 16 Esto ya nos permite ordenar matrices de una forma mucho más eficiente. También admite un segundo parámetro opcional, que coincide con el de la función sort(). La alternativa a estas dos funciones nos la proporcionan rsort() y arsort(), que actúan como las que ya hemos visto, pero invirtiendo el orden de los elementos. Las matrices pueden ordenarse no solo por el valor de sus elementos, sino también por la clave. La función ksort() hace, exactamente esto. Mira el listado ordenacion_ksort(): <?php $matriz = array("Nombre" => "Pedro", "Provincia"=>"Extremadura", "Profesion"=>"Sexador de pollos"); var_dump($matriz); ksort($matriz); var_dump($matriz); ?> Pruébalo en tu navegador para ver su comportamiento. Esta función también tiene una alternativa para ordenar por orden inverso, llamada, cómo no, krsot(). Ambas admiten un segundo parámetro opcional que coincide con los que ya conocemos. La función natsort() ordena los elementos por sus valores, manteniendo las asociaciones con las claves, siguiendo el orden "natural" de la ordenación. es una versión refinada de asort(). Para matrices unidimensionales es la que yo uso con más frecuencia en mi trabajo. Tienes un ejemplo de uso en ordenacion_natsort.php. <?php $matriz = array("img12.png", "img1.png"); var_dump($matriz); natsort($matriz); var_dump($matriz); ?> "img10.png", "img2.png", Esta función no admite segundo argumento, ni tiene ordenación inversa. Sin embargo, cuenta con una peculiaridad, que debemos conocer: distingue entre mayúsculas y minúsculas. Así, un elemento cuyo valor sea OMEGA será "menor" que uno cuyo valor sea alfa. Si deseas salvar esta cuestión, puedes usar natcasesort(), que no efectúa esta distinción. Podemos ordenar una matriz mediante una función de usuario. Esto permite establecer un criterio para la ordenación, definiéndolo en una función que nosotros mismos creamos para refinar el proceso según nuestras necesidades. Para ello podemos usar la función usort(), que recibe dos argumentos. El primero es el nombre de la matriz a ordenar y el segundo es el de la función que hemos definido para la ordenación. A continuación aparece el listado ordenarConFunciones.php: <?php $matriz = array("Luis", "Eva", "Alfonso", "Ana", "Carmen", "Hermenegildo", "Pedro"); function ordenar($a, $b){ $la = strlen($a); $lb = strlen($b); if ($la == $lb){ return 0; } elseif($la > $lb){ return 1; } else { return - 1; } } echo "MATRIZ ORIGINAL:<br />"; var_dump ($matriz); usort($matriz, "ordenar"); echo "MATRIZ ORDENADA:<br />"; var_dump ($matriz); ?> En la ilustración 17 vemos el resultado. Este código ordena los elementos por su longitud. No hay función predefinida de PHP que haga esto, así que, siempre que necesitemos una ordenación personalizada, como en este caso, recurriremos a esta función. En la misma familia de esta función, nos encontramos con otras que también realizan ordenaciones personalizadas. Por ejemplo, tenemos uksort(), que actúa de igual modo que esta pero, en lougar de hacer la ordenación sobre los valores de los elementos, la realiza sobre sus claves. Por supuesto, esta función sólo tiene sentido en matrices asociativas. Ilustración 17 Observa, en la ilustración 17, que se ha perdido la correlación entre claves y valores. Si necesitas que se mantenga, puedes recurrir a la función uasort() que, por lo demás, actúa igual que esta. Un punto y aparte en este capítulo lo constituye la ordenación de matrices multidimensionales. Supongamos que tenemos una lista de empleados de una empresa, así: array 0 => array 'dni' => string '349294' (length=6) 'nombre' => string 'Pedro' (length=5) 'apellidos' => string 'García Torres' (length=13) 1 => array 'dni' => string '425454' (length=6) 'nombre' => string 'Laura' (length=5) 'apellidos' => string 'Cifuentes de la (length=23) 2 => array 'dni' => string '789875' (length=6) 'nombre' => string 'Isabel' (length=6) 'apellidos' => string 'León Blanco' (length=11) 3 => array 'dni' => string '52434' (length=5) 'nombre' => string 'Rodrigo' (length=7) 'apellidos' => string 'Díaz de Vivar' (length=13) Higuera' Esta es una situación muy común en el mundo de las aplicaciones web. Ahora pensemos, por ejemplo, que queremos ordenar los datos de los empleados por el nombre de cada uno. Por supuesto, deberán mantenerse las correlaciones del nombre con los demás datos, para que no le asignemos, por ejemplo, a un empleado un DNI o unos apellidos que no le corresponden. La función que nos ayuda en este caso es array_multisort(). Para su uso, debemos crear una matriz temporal con los datos que queramos ordenar. Por ejemplo, si queremos ordenar, como hemos dicho, por el nombre, deberemos crear una matriz con los nombres de los empleados, que la función usará como pauta para la ordenación. Para ver esto más claro, mira el código ordenarMultidimensionales.php, listado a continuación: <?php $personal = array(); $empleado = array("dni"=>"349294", "nombre"=>"Pedro", "apellidos"=>"García Torres"); $personal[] = $empleado; $empleado = array("dni"=>"425454", "nombre"=>"Laura", "apellidos"=>"Cifuentes de la Higuera"); $personal[] = $empleado; $empleado = array("dni"=>"789875", "nombre"=>"Isabel", "apellidos"=>"León Blanco"); $personal[] = $empleado; $empleado = array("dni"=>"52434", "nombre"=>"Rodrigo", "apellidos"=>"Díaz de Vivar"); $personal[] = $empleado; echo "MATRIZ ANTES DE ORDENAR:<br />"; var_dump($personal); foreach ($personal as $clave => $empleado) { $nombres[$clave] = $empleado["nombre"]; } array_multisort($nombres, SORT_ASC, $personal); echo "MATRIZ DESPUÉS DE ORDENAR:<br />"; var_dump($personal); ?> Tras la ordenación, la matriz de empleados quedará así: array 0 => array 'dni' => string '789875' (length=6) 'nombre' => string 'Isabel' (length=6) 'apellidos' => string 'León Blanco' (length=11) 1 => array 'dni' => string '425454' (length=6) 'nombre' => string 'Laura' (length=5) 'apellidos' => string 'Cifuentes de la (length=23) 2 => array 'dni' => string '349294' (length=6) 'nombre' => string 'Pedro' (length=5) 'apellidos' => string 'García Torres' (length=13) 3 => array 'dni' => string '52434' (length=5) 'nombre' => string 'Rodrigo' (length=7) 'apellidos' => string 'Díaz de Vivar' (length=13) Higuera' Prueba el código, incluido en el paquete de los que acompañan a este tema, en tu navegador para ver como actúa. OTROS PROCESOS CON MATRICES Con las matrices en PHP podemos hacer varias cosas más. Por ejemplo, unir matrices, separarlas, convertirlas en cadenas... En este apartado veremos esas funcionalidades. Empezaremos con una muy útil en gran cantidad de ocasiones. Suponte que tienes que pasar una matriz de PHP a JavaScript, dentro de tu página, para ser procesada en el lado del cliente. No es fácil. PHP y JavaScript no siempre se comunican bien, y pasar matrices directamente no funciona. En ese caso, convertiremos la matriz PHP a una cadena, y le pasaremos esta a JavaScript. Luego, dentro de JavaScript ya convertiremos de nuevo la cadena en matriz, para procesarla. En este temario no vamos a ver como opera JavaScript (lo que te he mencionado es sólo un ejemplo y, si estás leyendo sobre PHP, doy por sentado que ya estás familiarizado con JS). Pero sí veremos cómo convertir matrices a cadenas con la función implode(). Esta recibe, como argumentos, un separador (que, normalmente, suele ser una coma, aunque puede ser el que tú necesites en cada caso) y el nombre de la matriz que deseamos convertir en una cadena. La función devuelve, precisamente, la cadena, así: $cadena = implode("-", $matriz); Esta línea cogerá los elementos de la matriz y los dispondrá en una cadena, separando cada uno del siguiente mediante un guión. El separador es un argumento opcional. Si no lo pones, PHP usará, como separador una cadena vacía. Vamos, que pondrá todos los elementos uno a continuación de otro, sin separación (no tiene mucho sentido práctico, la verdad). Como contraria a esta, tienes la función explode(), que, a partir de una cadena, te obtiene elementos de una matriz. La cadena la divide, también, con el separador que le indiques, así: $matriz = explode("-", $cadena); El separador no tiene por qué ser un único carácter. Hay ocasiones en las que deberemos usar (por cuestiones de visualización u otras circunstancias) más de un carácter. Por supuesto, hay que tener en cuenta esto a la hora de reconstruir la matriz. Lo mejor es ver todo en un ejemplo práctico, para entender cómo actúan estas funciones. Mira el listado de matriz_a_cadena.php: <?php // Creamos una matriz (podría proceder de cualquier proceso, lectura de BBDD, etc) $ciudades = array("Madrid", "Barcelona", "París", "Londres", "Roma", "Bucarest"); echo "La matriz es la siguiente:<br />"; var_dump($ciudades); // Convertimos la matriz en una cadena y la mostramos para ver que se ha convertido correctamente. $listaDeCiudades = implode(" - ", $ciudades); echo "<br />La lista de ciudades es la siguiente: ".$listaDeCiudades."<br /><br />"; // Creamos otra matriz a partir de un fragmentado (explode) de la cadena. $cities = explode(" - ", $listaDeCiudades); echo "La nueva matriz de ciudades es la siguiente:<br />"; var_dump($cities); ?> El resultado lo puedes ver en la ilustración 18, a continuación. Obsérvalo sin perder de vista el código para comprender como opera. Ilustración 18 Otra posibilidad que tenemos con las matrices es la combinación de dos matrices en una tercera, de modo que los elementos de la primera constituirán las claves y los de la segunda los valores. Esto se hace mediante la función array_combine(), que recibe dos argumentos. El primero es la matriz que compondrá las claves y el segundo la que aportará los valores. Lo verás más claro en el listado combinar_matrices.php (mira el resultado en la ilustración 19): <?php // Creamos una matriz que aportará las claves. $identificadores = array("MA", "BA", "PA", "LO", "RO", "BU"); echo "La matriz de identificadores de ciudades es la siguiente:<br />"; var_dump($identificadores); // Creamos una matriz que aportará los valores. $ciudades = array("Madrid", "Barcelona", "París", "Londres", "Roma", "Bucarest"); echo "La matriz de nombres de ciudades es la siguiente:<br />"; var_dump($ciudades); // Combinamos ambas matrices. $cities = array_combine($identificadores, $ciudades); echo "La matriz combinada de ciudades es la siguiente:<br />"; var_dump($cities); ?> Ilustración 19 Es importante que tengas en cuenta que ambas matrices deben tener el mismo número de elementos. Si no, se genera una excepción (un error, para entendernos). Otra forma de combinar matrices es añadiendo los elementos de una a otra, de forma que, como resultado, obtenemos una matriz con los elementos de las dos. Para ello, contamos con la función array_merge(), que recibe, como argumentos, los nombres de las matrices que quieras anexionar (dos, tres o las que sean). Solo el primer argumento (la primera matriz) es obligatorio. Sin embargo, claramente, no tiene sentido usar esta función con menos de dos matrices. También debes tener en cuenta que, si alguno de los argumentos no es una matriz, deberás hacer que aparezca como tal, mediante un casting de datos. Veamos todo esto en el script uso_de_merge.php: <?php // Creamos una matriz de ciudades de Europa. $ciudadesEuropa = array("Madrid", "Barcelona", "París", "Londres", "Roma", "Bucarest"); echo "Ciudades de Europa:<br />"; var_dump($ciudadesEuropa); // Creamos una matriz de ciudades de América. $ciudadesAmerica = array("Buenos Aires", "Río de Janeiro", "Wichita", "New York", "Tombstone"); echo "Ciudades de América:<br />"; var_dump($ciudadesAmerica); // Unimos las dos matrices. $cities = array_merge($ciudadesEuropa, $ciudadesAmerica); echo "Ciudades de Europa y América:<br />"; var_dump($cities); Ilustración 20 ?> En la ilustración 20 puedes ver el resultado. A la hora de emplear esta función debes tener en cuenta los siguientes puntos: • Si las claves de las matrices son asociativas y en ambas matrices hay algún elemento con la misma clave, el que pertenezca a la primera matriz será sobrescrito. • Si las claves son numéricas, se numeran consecutivamente como ves en el ejemplo. En ocasiones es interesante determinar la diferencia entre dos matrices (hace poco se me ha presentado un caso en el que tenía, en una matriz, los empleados de una empresa y en la otra aquellos empleados que no estaban al día en el cobro de sus nóminas, y tenía que obtener la lista de los que sí habían cobrado). Para esto usamos la función array_diff(). Esta función recibe el nombre de dos o más matrices y devuelve una matriz con aquellos elementos de la primera que no están en ninguna de las otras. Veamos un ejemplo en resta_matrices.php: <?php Ilustración 21 $empleados = array("Pedro", "Laura", "Manuel", "Alfonso", "Verónica", "Alicia", "Luis", "Beatriz"); echo "La matriz de empleados es:<br />"; var_dump($empleados); $empleadosConAtrasos = array("Alfonso", "Laura", "Luis"); echo "La matriz de empleados con atrasos salariales es:<br />"; var_dump($empleadosConAtrasos); $empleadosAlCorriente = array_diff($empleados, $empleadosConAtrasos); echo "La matriz de empleados al corriente de cobro es:<br />"; var_dump($empleadosAlCorriente); ?> Puedes ver el resultado en la ilustración 21. Una función muy interesante es array_walk(). Permite recorrer todos los elementos de una matriz aplicándoles una función definida por nosotros. Esta función recibe dos argumentos obligatorios: el primero es el nombre de la matriz sobre la que vamos a operar y el segundo el de la función que va a procesar los datos de la matriz. Además, podemos pasarle argumentos opcionales que array_walk() se encarga de pasar a la función de usuario con la que vamos a trabajar. INCISO. En un artículo posterior hablaremos de funciones definidas por el usuario en PHP. Supongamos una matriz de líneas de facturación como la siguiente: array 0 => array 'ref' => string '12345T' (length=6) 'articulo' => string 'mochila azul' (length=12) 'precio' => float 23.4 'cantidad' => int 5 1 => array 'ref' => string 'yur483' (length=6) 'articulo' => string 'mocasines' (length=9) 'precio' => float 37.18 'cantidad' => int 6 2 => array 'ref' => string 'FR4532' (length=6) 'articulo' => string 'camisa roja' (length=11) 'precio' => float 17.1 'cantidad' => int 21 3 => array 'ref' => string 'eio32001w' (length=9) 'articulo' => string 'llavero mosquetón' (length=17) 'precio' => float 0.37 'cantidad' => int 34 Cómo ves, es una matriz bidimensional. Cada fila tiene una clave indexada y, dentro de esta, cada elemento tiene una clave asociativa. Ahora pensemos lo siguiente: necesitamos recorrer cada elemento, calculando el importe total de la correspondiente línea de detalle. Para ello, debemos hacer dos cosas: multiplicar el precio unitario que figura en la matriz por la cantidad de elementos de cada línea y calcular el correspondiente 21 % de IVA. Además, le añadiremos un número de línea a cada elemento. La matriz, después de esto, quedará así: array 0 => array 'ref' => string '12345T' (length=6) 'articulo' => string 'mochila azul' (length=12) 'precio' => float 23.4 'cantidad' => int 5 'importe' => string '117,00' (length=6) 'importe_iva' => string '24,57' (length=5) 'lineaDeDetalle' => int 1 1 => array 'ref' => string 'yur483' (length=6) 'articulo' => string 'mocasines' (length=9) 'precio' => float 37.18 'cantidad' => int 6 'importe' => string '223,08' (length=6) 'importe_iva' => string '46,83' (length=5) 'lineaDeDetalle' => int 2 2 => array 'ref' => string 'FR4532' (length=6) 'articulo' => string 'camisa roja' (length=11) 'precio' => float 17.1 'cantidad' => int 21 'importe' => string '359,10' (length=6) 'importe_iva' => string '75,39' (length=5) 'lineaDeDetalle' => int 3 3 => array 'ref' => string 'eio32001w' (length=9) 'articulo' => string 'llavero mosquetón' (length=17) 'precio' => float 0.37 'cantidad' => int 34 'importe' => string '12,58' (length=5) 'importe_iva' => string '2,52' (length=4) 'lineaDeDetalle' => int 4 El script uso_de_walk.php hace esto, así: <?php $factura = array(array("ref"=>"12345T", "articulo"=>"mochila azul", "precio"=>23.40, "cantidad"=>5), array("ref"=>"yur483", "articulo"=>"mocasines", "precio"=>37.18, "cantidad"=>6), array("ref"=>"FR4532", "articulo"=>"camisa roja", "precio"=>17.10, "cantidad"=>21), array("ref"=>"eio32001w", "articulo"=>"llavero mosquetón", "precio"=>0.37, "cantidad"=>34)); var_dump($factura); function ponerImporte(&$elemento, $clave, $iva){ $elemento["importe"] = number_format((float)($elemento["precio"] * $elemento["cantidad"]), 2, ",", "."); $elemento["importe_iva"] = number_format((float)(($elemento["importe"] * $iva) / 100), 2, ",", "."); $elemento["lineaDeDetalle"] = $clave + 1; } array_walk($factura, "ponerImporte", 21.00); var_dump($factura); ?> Como ves, la función array_walk() recibe, en este ejemplo, tres argumentos. El primero es el nombre de la matriz con la que vamos a trabajar. El segundo argumento es el nombre de la función de usuario (que definiremos nosotros mismos en el código) que va a procesar cada item de la matriz. Estos dos primeros siempre son obligatorios. El tercer argumento (y más que hubiera si fueran necesarios) son valores que pasarán, a su vez, a la función de usuario para su trabajo. En este momento no vamos a detallar como opera la función de usuario sobre la matriz. Hablaremos de ello en el artículo en el que tratemos el tema d funciones personalizadas definidas por el usuario (en realidad, por el programador, claro). Te sugiero que, tras leer ese artículo, veas cómo opera array_walk(). Nota: Hemos visto la mayoría de las posibilidades de las matrices. No obstante, me voy a permitir aconsejarte que eches un vistazo a la documentación oficial de PHP (http://www.php.net/manual/es/). Es muy exhaustiva y puedes aprender algo más. Además, tiene la ventaja de que está siendo permanentemente actualizada.