Revista Tecnología

Lectura, escritura y eliminación de elementos de un array multidimensional en PHP usando separadores

Publicado el 19 octubre 2015 por Gaspar Fernández Moreno @gaspar_fm

Lectura, escritura eliminación elementos array multidimensional usando separadoresLectura, escritura y eliminación de elementos de un array multidimensional en PHP usando separadores

Puede parecer muy complejo así dicho. Pero de lo que se trata es de proporcionar una forma más natural para acceder a lo elementos de un array en PHP. Nos podemos imaginar un array de configuración de una aplicación, donde encontremos apartados como cookies, idiomas, usuarios, rutas, urls, apis externas, bases de datos e infinidad de cosas más. Hace un tiempo veíamos una función para acceder a una clave de un array, comprobando antes la existencia de esa clave y dándonos la opción de devolver un valor por defecto en caso de que dicha clave no exista.

Nota: si ya eres usuario de un Framework PHP, seguramente tengas métodos para hacer esto mismo, y ya estará todo hecho, a lo mejor con más opciones, aunque si quieres ver cómo está hecho, continúa leyendo.

Lectura, escritura eliminación elementos array multidimensional usando separadores

Lectura, escritura y eliminación de elementos de un array multidimensional en PHP usando separadores

Aquí recuerdo dicha función (porque el post anterior es muy largo):

De esta forma, en lugar de hacer $array['clave'] para leer un valor del array, haremos av($array, 'clave', 123), de esta forma podremos devolver 123 en caso de que la clave del array no exista, además de verificar la existencia de la clave previa al acceso, con lo que ahorraremos tiempo de ejecución (no es una eternidad, pero todo lo que podamos optimizar, mejor). En el post vemos que la mejor forma es no utilizar la función pero sí comprobar la existencia de la variable, aunque de esta forma estaremos escribiendo el array y la clave dos veces, y de cara a la programación eso no es muy útil y puede dar lugar a errores.

A lo que vamos hoy es a un array como el siguiente:

Sería muy interesante poder acceder al nivel del usuario carlos especificando la ruta: "usuarios/carlos/nivel". Por un lado, podemos pensar que es inmediato hacer $datos['usuarios']['carlos']['nivel']. Pero tenemos el problema de comprobar que todos los elementos de la ruta existen (si miráis el array, no todos los usuarios tienen nivel, por lo que puede haber un nivel por defecto para todos los usuarios que no lo tengan, y así ahorramos algo de memoria; por lo que para poder acceder correctamente, es decir, sin producir errores, deberíamos hacer lo siguiente:

y eso es muy largo. Es más, no es muy costoso generar la cadena "usuarios/carlos/nivel" o "usuarios.carlos.nivel" y pasársela a una función tipo:

es más, dicha cadena puede ser construida de forma fácil y dinámica, por ejemplo con los valores de un formulario. En fin, tiene muchas posibilidades.

Dejo aquí un ejemplo completo para la función ag(), para hacer copia y pega directamente (el array utilizado varía ligeramente del anterior, para hacerlo un poco más largo y meter índices numéricos, sólo por hacer un test más):

Un vistazo a la función

¿ Cómo estamos haciendo esto ?

La clave de la realización de esta función radica en la separación de los elementos de la ruta (con explode() y el hacer corresponder cada uno de los elementos de la ruta con las claves del array, para eso usamos el foreach(). Para cada $k que obtenemos, realizamos la comprobación de existencia de dicha clave en el array de datos, en caso de no existir, sabemos que ya no vamos a encontrar más elementos de la ruta, y por tanto, devolvemos el valor por defecto; en caso contrario (si la clave existe en el array), haremos que el array con el que trabajamos se reduzca al elemento $k de nuestro array.

¡ Queremos algo más !

Vale, pues qué tal la creación y eliminación de elementos dentro de ese array. Es decir, que con una sola línea de código podamos dar valor a la ruta "datos/colecciones/0/felipe/email", es decir, debería crearse el elemento "felipe" y luego el elemento "email", o, complicándolo más, podemos crear la ruta: "datos/config/database/username, es decir, crear config, database y username, eso sí, aunque eso podríamos hacerlo de forma normal:

podríamos tener un problema si no sabemos seguro si algún elemento de la cadena existe, por ejemplo, que 'config' pueda existir o no.

Para ello, tenemos la función aset():

Aquí, la clave está en que, en lugar de machacar nuestro array con $data[$k], estamos diciendo que $_data coja la referencia de $_data[$key], es decir, el array entero se mantiene en memoria, nosotros simplemente navegamos por él, por el árbol original, y no por copias de sub-árboles (y todo esto, sólo poniendo un &, esto se hace mucho en C, en PHP no tanto, pero podemos).

Para hacer una pequeña prueba podemos:

símplemente, copiando la función y estas líneas en el ejemplo de ag().

¿Y si quiero borrar una rama?

Pues también podemos, de una forma muy parecida,

Y también dejo algo de código para probar esta función:

Algunas notas más

Como vemos, estas funciones tienen un argumento llamado $sep, que por defecto vale una barra "/", aunque no es necesario utilizarlo, puede que nos interese separar los elementos de la ruta por un punto en lugar de una barra, basta con poner ese valor a ".".

Aunque las funciones no son especialmente lentas, si vamos a hacer muchas llamadas (imaginemos dentro de un array), es inevitable que se retrase la ejecución del código, por lo que es una buena técnica que en lugar de esto:

hagáis esto:

Por lo que la búsqueda grande sólo la hacemos una vez... y, si de verdad estamos seguros de que el usuario devuelve todos los datos sin problema, podemos prescindir de av() y hacer $usuario['nombre'], $usuario['nivel']...

Por último, indicar que todos los nombres (con apellidos) utilizados en los ejemplos son inventados, o de personas famosas (lo digo por ernest@hemingway, si os fijáis, quitando Administrador, el nombre que empieza por B, su apellido por Z, el que empieza por C, su apellido por Y, y así sucesivamente.

Foto: Carrie Roy (Flickr CC)


Volver a la Portada de Logo Paperblog