Curso de PHP gratis · Capítulo 7 de 13 · con Leo Ordenado
Arrays en PHP: listas, asociativos y foreach
El array es la estructura de datos estrella de PHP: sirve como lista, diccionario y tabla. Verás cómo funciona por dentro y por qué asignarlo lo copia.
Listas
Un array se crea con corchetes: $frutas = ["manzana", "pera", "uva"];
Cada valor tiene una clave (o índice). En una lista, las claves son números que empiezan en 0: $frutas[0] es "manzana" y $frutas[2] es "uva".
count($frutas): cuántos elementos hay.$frutas[] = "kiwi";: añade al final.$frutas[1] = "fresa";: cambia un elemento.
El último elemento está en count($a) - 1, no en count($a). Leer una clave que no existe da un Warning (Undefined array key).
Con print_r() puedes ver un array entero (y var_dump() también, con tipos).
<?php
$frutas = ["manzana", "pera", "uva"];
echo $frutas[0], " y ", $frutas[2], "\n";
$frutas[] = "kiwi";
$frutas[1] = "fresa";
echo count($frutas), " frutas\n";
print_r($frutas);manzana y uva
4 frutas
Array
(
[0] => manzana
[1] => fresa
[2] => uva
[3] => kiwi
)Arrays asociativos
Las claves no tienen por qué ser números: pueden ser strings. Es un array asociativo, perfecto para datos con nombre:
$precios = [ "heno" => 2, "zanahoria" => 5, ]; echo $precios["zanahoria"]; // 5
El operador => asocia cada clave con su valor. Para añadir o cambiar: $precios["calabaza"] = 12;. Para borrar: unset($precios["heno"]);.
Para comprobar si una clave existe: isset($precios["kiwi"]) o array_key_exists("kiwi", $precios). Y el operador ?? te da un valor por defecto sin avisos: $precios["kiwi"] ?? 0.
<?php
$precios = [
"heno" => 2,
"zanahoria" => 5,
];
$precios["calabaza"] = 12;
$precios["heno"] = 3;
echo "Una calabaza cuesta ", $precios["calabaza"], "\n";
echo "Un kiwi cuesta ", $precios["kiwi"] ?? "(no hay)", "\n";
print_r($precios);Una calabaza cuesta 12
Un kiwi cuesta (no hay)
Array
(
[heno] => 3
[zanahoria] => 5
[calabaza] => 12
)¿Qué es REALMENTE un array?
En PHP no hay "listas" y "diccionarios" por separado: todo array es un mapa ordenado (por dentro, una tabla hash que además recuerda el orden de inserción). Una lista no es más que un array cuyas claves resultan ser 0, 1, 2...
Esto explica varias rarezas:
- Las claves solo pueden ser int o string. Si usas
"7", PHP la convierte en el entero 7;truese convierte en 1. - El orden es el de inserción, no el de las claves.
$a[] = xusa como clave el mayor entero usado hasta ahora + 1.- Al hacer
unset, las demás claves no se renumeran.
¿Por qué PHP eligió un único tipo de array tan flexible? Porque nació para la web, donde casi todo son listas de datos con nombre (formularios, filas de una base de datos...). Un solo tipo que sirve para todo simplifica mucho el lenguaje, a cambio de algunas sorpresas como estas.
<?php
$raro = [5 => "a", "b", "7" => "c", "d", true => "e"];
print_r($raro);
$lista = ["x", "y", "z"];
unset($lista[1]);
$lista[] = "w";
print_r($lista);
print_r(array_values($lista));Array
(
[5] => a
[6] => b
[7] => c
[8] => d
[1] => e
)
Array
(
[0] => x
[2] => z
[3] => w
)
Array
(
[0] => x
[1] => z
[2] => w
)Tras $a = [3 => "x"]; $a[] = "y";, ¿cuál es la clave de "y"?
- 0
- 1
- 4
- "y"
Ver respuesta
C. 4
La siguiente clave automática es la mayor clave entera usada + 1: 3 + 1 = 4.
foreach: recorrer un array
foreach recorre un array elemento a elemento, en orden:
foreach ($array as $valor) { ... } foreach ($array as $clave => $valor) { ... }
En cada vuelta, $valor recibe una copia del elemento actual (y $clave su clave). No necesitas contador ni saber cuántos elementos hay.
Es la forma más habitual de recorrer arrays en PHP. El for con índice solo funciona bien con listas de claves 0, 1, 2... seguidas.
<?php
$inventario = ["heno" => 40, "zanahoria" => 7, "madera" => 12];
$total = 0;
foreach ($inventario as $producto => $cantidad) {
echo "$producto: $cantidad\n";
$total += $cantidad;
}
echo "Total: $total\n";heno: 40 zanahoria: 7 madera: 12 Total: 59
Los arrays se copian
Esto es fundamental: los arrays en PHP tienen semántica de valor. $b = $a; crea una copia: modificar $b no toca $a. Igual al pasarlos a una función: la función recibe su propia copia.
Pero... ¿copiar un array de un millón de elementos no es lentísimo? Aquí entra un truco del motor Zend llamado copy-on-write (copiar al escribir). $b = $a NO copia nada: las dos variables apuntan al MISMO array y PHP apunta cuántas variables lo usan (su refcount). Solo cuando alguien intenta modificarlo, y el refcount es mayor que 1, PHP hace la copia de verdad (separa el array).
Resultado: te comportas como si hubiera copia (seguro), pero solo se paga cuando hace falta (rápido). Por eso en PHP no hace falta pasar arrays "por referencia" para ganar velocidad.
Mira la Lupa: tras $b[] = "maíz", cada variable tiene su propio array.
<?php
$a = ["heno", "trigo"];
$b = $a;
$b[] = "maíz";
echo count($a), " vs ", count($b), "\n";
print_r($a);2 vs 3
Array
(
[0] => heno
[1] => trigo
)Arrays dentro de arrays
Un valor de un array puede ser otro array. Así se representan tablas y mapas en 2D:
$mapa[$y][$x] = "hierba";
El primer índice elige la fila, el segundo la columna. Para recorrerlo, un foreach dentro de otro.
Puedes crear arrays anidados "sobre la marcha": si $mapa no existe, $mapa[0][2] = "x"; crea todo lo necesario.
<?php
$campo = [
["hierba", "zanahoria"],
["arbol", null],
];
echo $campo[1][0], "\n";
$campo[1][1] = "girasol";
foreach ($campo as $y => $fila) {
foreach ($fila as $x => $planta) {
echo "($x,$y)=$planta ";
}
echo "\n";
}arbol (0,0)=hierba (1,0)=zanahoria (0,1)=arbol (1,1)=girasol
Funciones de arrays
Algunas de las más usadas:
in_array($v, $a),array_search($v, $a): buscar un valor.array_keys($a),array_values($a): solo claves / solo valores.array_sum,max,min,count.implode(", ", $a): une en un string.explode(",", $s): parte un string en array.array_merge($a, $b),array_slice($a, $inicio, $n),array_reverse($a).- Ordenar:
sort/rsort(por valor, pierden las claves),asort/arsort(por valor, conservan claves),ksort/krsort(por clave).
Las funciones de ordenar modifican el array original y devuelven true. $ordenado = sort($a); es un error clásico: $ordenado vale true. (En el capítulo 10 verás cómo lo hacen: con una referencia.)
<?php
$precios = ["heno" => 2, "calabaza" => 12, "zanahoria" => 5];
var_dump(in_array(5, $precios));
echo array_search(12, $precios), "\n";
echo implode(", ", array_keys($precios)), "\n";
echo "Suma: ", array_sum($precios), ", máximo: ", max($precios), "\n";
arsort($precios);
print_r($precios);
$palabras = explode(" ", "cada cosa en su sitio");
echo count($palabras), " palabras\n";bool(true)
calabaza
heno, calabaza, zanahoria
Suma: 19, máximo: 12
Array
(
[calabaza] => 12
[zanahoria] => 5
[heno] => 2
)
5 palabrasDesestructurar
Puedes "desempaquetar" un array en variables con [...] = :
[$x, $y] = [3, 5]; ["nombre" => $n, "edad" => $e] = $persona;
Un truco muy elegante para intercambiar dos variables sin una variable auxiliar: [$a, $b] = [$b, $a];.
Funciona también en foreach: foreach ($puntos as [$x, $y]).
<?php
[$x, $y] = [3, 5];
echo "x=$x y=$y\n";
[$x, $y] = [$y, $x];
echo "x=$x y=$y\n";
$puntos = [[1, 2], [3, 4]];
foreach ($puntos as [$a, $b]) {
echo $a + $b, " ";
}
echo "\n";x=3 y=5 x=5 y=3 3 7
Ejercicios de Arrays en PHP resueltos
Intenta resolverlos antes de mirar la solución. En el juego puedes ejecutarlos y comprobarlos automáticamente.
Encargo 1. La lista de precios
Ordenado tiene sus precios en $precios (producto => precio). Quiere un cartel.
Imprime una línea por producto con el formato producto: N monedas y al final Total: N monedas con la suma. Por ejemplo:
heno: 2 monedas zanahoria: 5 monedas calabaza: 12 monedas Total: 19 monedas
Pistas
foreach ($precios as $producto => $precio) { ... }- Para el total puedes sumar en el bucle o usar
array_sum($precios).
Ver solución
<?php
foreach ($precios as $producto => $precio) {
echo "$producto: $precio monedas\n";
}
echo "Total: ", array_sum($precios), " monedas\n";
Misión del dron 2. El mapa del campo
Ordenado quiere un mapa de su huerto de 3x3 guardado en un array, para consultarlo sin mandar a BIT-8 cada vez.
Recorre el campo y construye $mapa de forma que $mapa[$y][$x] sea lo que devuelve que_hay() en la casilla (x, y). (Las casillas vacías tendrán null.)
Esta misión mueve al dron BIT-8 por un campo de cultivo: juégala en PHPlay para ver tu código animado.
Pistas
- Dos bucles
forcon$yy$x, igual que para cosechar el campo entero. - Dentro:
$mapa[$y][$x] = que_hay();y luegomover(ESTE);.
Ver solución
<?php
$mapa = [];
for ($y = 0; $y < 3; $y++) {
for ($x = 0; $x < 3; $x++) {
$mapa[$y][$x] = que_hay();
mover(ESTE);
}
mover(NORTE);
}
Predicción 3. ¿Quién tiene qué?
Ordenado reparte cajas entre tres ayudantes. ¿Cuántas acaba teniendo cada uno?
Escribe exactamente la salida.
<?php
$a = [1, 2, 3];
$b = $a;
$b[] = 4;
$c = $b;
$c[0] = 99;
echo count($a), count($b), " ", $a[0], " ", $c[0];Ver solución
34 1 99
Cada asignación es una copia (aunque, por dentro, PHP no copia hasta que alguien escribe).
Caza de bugs 4. El sort traicionero
El ayudante de Ordenado quería imprimir los precios ordenados de menor a mayor, pero solo sale un 1.
Arregla el programa para que imprima 2, 5, 7, 12.
Código de partida:
<?php
$precios = [12, 5, 7, 2];
$ordenados = sort($precios);
echo implode(", ", $ordenados);
Pistas
sort()ordena el array que le pasas en el sitio y devuelvetrue.- Llama a
sort($precios);y después imprime$precios.
Ver solución
<?php
$precios = [12, 5, 7, 2];
sort($precios);
echo implode(", ", $precios);
Encargo 5. El mensaje más largo
Nadia Cadena le ha pasado a Ordenado un telegrama en $frase. Quiere estadísticas.
Imprime tres líneas: el número de palabras, la palabra más larga (si hay empate, la primera) y las palabras en orden inverso separadas por espacios:
Palabras: 5 Más larga: cosechado Al revés: sacos doce cosechado ha BIT-8
Pistas
count($palabras)para el número de palabras.- Para la más larga, recorre con
foreachy guarda la palabra si sustrlenes mayor que la mejor hasta ahora. implode(" ", array_reverse($palabras)).
Ver solución
<?php
$palabras = explode(" ", $frase);
echo "Palabras: ", count($palabras), "\n";
$larga = "";
foreach ($palabras as $p) {
if (strlen($p) > strlen($larga)) {
$larga = $p;
}
}
echo "Más larga: $larga\n";
echo "Al revés: ", implode(" ", array_reverse($palabras)), "\n";
Misión del dron 6. Girasoles de oro
El girasol con MÁS pétalos del campo da 5 de energía al cosecharlo; los demás, solo 1. Pero cuando cosechas el mayor, ¡otro pasa a ser el mayor!
El campo 3x3 está lleno de girasoles maduros. Consigue al menos 40 de energía. Idea: mide todos, guarda los pétalos en un array, ordénalos de mayor a menor y cosecha en ese orden.
Esta misión mueve al dron BIT-8 por un campo de cultivo: juégala en PHPlay para ver tu código animado.
Pistas
- Primer recorrido:
$petalos[] = medir();en cada casilla. rsort($petalos);los deja de mayor a menor.- Luego, por cada valor: recorre el campo y cosecha solo si
medir() === $valor.
Ver solución
<?php
$n = tam();
$petalos = [];
for ($y = 0; $y < $n; $y++) {
for ($x = 0; $x < $n; $x++) {
$petalos[] = medir();
mover(ESTE);
}
mover(NORTE);
}
rsort($petalos);
foreach ($petalos as $objetivo) {
for ($y = 0; $y < $n; $y++) {
for ($x = 0; $x < $n; $x++) {
if (medir() === $objetivo) {
cosechar();
}
mover(ESTE);
}
mover(NORTE);
}
}
Chuleta: lo esencial
- Lista:
$a = [1, 2, 3];claves 0, 1, 2.$a[] = x;añade.count($a). - Asociativo:
$p = ["heno" => 2];$p["kiwi"] ?? 0,isset($p["k"]),unset($p["k"]). - Todo array es un mapa ordenado: claves int|string, orden de inserción,
$a[]usa la mayor clave entera + 1,unsetno renumera. foreach ($a as $v)/foreach ($a as $k => $v):$ves una copia.- Los arrays se copian al asignar y al pasarlos a funciones. Por dentro: copy-on-write con refcount.
- 2D:
$mapa[$y][$x]. in_array,array_search,array_keys,array_values,array_sum,max,min,implode,explode,array_merge,array_slice,array_reverse.sort/rsort(pierden claves),asort/arsort(las conservan),ksort/krsort(por clave). Modifican el original y devuelventrue.- Desestructurar:
[$a, $b] = [$b, $a];