Diccionarios

Cuando la clave es el dato

Marco González Luengo

Tabla de contenidos
  1. La clave es el dato
  2. El orden es el de llegada
  3. Tres trampas que hay que ver ejecutadas
  4. 1. isset miente con null
  5. 2. Leer una clave que no existe
  6. 3. PHP normaliza las claves
  7. Dos formas de preguntar si algo existe
  8. Diccionarios dentro de diccionarios
  9. Para practicar

En Arrays vimos que un array de PHP puede funcionar como diccionario, y en Pilas y colas quedó como la tercera estructura: la que sirve para buscar por identificador. Ahora toca mirarlo con calma, porque tiene sus mañas.

La clave es el dato

En una lista, la posición es un número sin significado: que un producto esté en la posición 2 no dice nada del producto. En un diccionario, la clave es parte del problema: el SKU de un producto, el RUT de una persona, la fecha de una venta.

<?php
declare(strict_types=1);

$stock = [
    'A-100' => 12,
    'B-205' => 0,
    'C-330' => 7,
];

// Acceso directo por clave: no recorre nada
echo $stock['B-205'], PHP_EOL;  // 0

foreach ($stock as $sku => $cantidad) {
    printf("%-6s %d\n", $sku, $cantidad);
}

printf muestra texto con formato. Se le pasa una plantilla y los valores que van en ella:

El orden es el de llegada

Un array de PHP conserva el orden en que se insertaron las claves. No se ordena solo, ni alfabética ni numéricamente:

<?php
declare(strict_types=1);

$stock = [];
$stock['C-330'] = 7;
$stock['A-100'] = 12;
$stock['B-205'] = 0;

print_r($stock);  // C, A, B: el orden en que llegaron

ksort($stock);    // ahora sí, ordenado por clave
print_r($stock);

Si necesitas el diccionario ordenado por clave, está ksort. Fíjate que, igual que sort, lo ordena en su lugar.

Tres trampas que hay que ver ejecutadas

1. isset miente con null

isset no pregunta "¿existe la clave?", sino "¿existe y no es null?". Si el valor guardado es null, dice que no está:

<?php
$config = ['debug' => null];

var_dump(isset($config['debug']));              // false (!)
var_dump(array_key_exists('debug', $config));   // true

2. Leer una clave que no existe

Ya lo vimos en Arrays: un warning, un null, y el programa sigue. Con ?? se resuelve dando un valor por defecto:

<?php
$config = ['debug' => null];

echo $config['idioma'], PHP_EOL;          // Warning + null
echo $config['idioma'] ?? 'es', PHP_EOL;  // es

3. PHP normaliza las claves

Las claves solo pueden ser números enteros o textos, así que PHP convierte lo demás. El texto '1' se convierte en el número 1, y true también. Resultado: estas tres asignaciones usan la misma clave y se pisan entre sí:

<?php
$r = [];
$r['1'] = 'a';
$r[1] = 'b';
$r[true] = 'c';

var_dump(count($r));  // 1
var_dump($r);         // una sola clave, 1, con el último valor

Esto importa cuando las claves vienen de datos: un código '1' leído de un archivo y un 1 calculado van a caer en el mismo casillero.

Dos formas de preguntar si algo existe

<?php
declare(strict_types=1);

$stock = ['A-100' => 12, 'B-205' => 0, 'C-330' => 7];

// Recorriendo: revisa las claves una por una
var_dump(in_array('C-330', array_keys($stock), true));

// Preguntando por la clave: va directo
var_dump(isset($stock['C-330']));

Las dos dicen true, pero no hacen lo mismo:

Cuánto cuesta cada una lo medimos en Eficiencia. Adelanto: la diferencia crece con la cantidad de datos.

Diccionarios dentro de diccionarios

El valor de una clave puede ser otro array. Así se arma algo parecido a una pequeña base de datos, donde cada usuario se busca por su nombre de usuario:

<?php
// Usuario
// nombre_usuario
// - nombres
// - apellidos
// - rut
// - fecha_nacimiento
// - domicilio

$base_usuarios = [
    "jcbodoque" => [
        "nombres" => "Juan Carlos",
        "apellidos" => "Bodoque Bodoque",
        "rut" => "15.432.876-9",
        "fecha_nacimiento" => 19900412,
        "domicilio" => "Av. Siempreviva 742",
    ],
    "chernobog" => [
        "nombres" => "Andrés",
        "apellidos" => "Palma Cerna",
        "rut" => "20.111.222-2",
        "fecha_nacimiento" => 20100315,
        "domicilio" => "Las Comadres 33-B",
    ],
    "sussynot32" => [
        "nombres" => "Susana Nicole",
        "apellidos" => "Jiménez Caro",
        "rut" => "9.876.543-3",
        "fecha_nacimiento" => 19670527,
        "domicilio" => "Tricolor de Paine 1880",
    ],
];

$busqueda = "jcbodoque";
//$busqueda = "estequiometrico";

echo array_key_exists($busqueda, $base_usuarios)
    ? "El usuario {$busqueda} se llama {$base_usuarios[$busqueda]['nombres']}."
    : "El usuario {$busqueda} no lo conoce ni Santa Isabel.";

Hay dos cosas nuevas aquí:

Prueba a descomentar la otra $busqueda.

Para practicar

Partiendo de la base de usuarios:

  1. Agrega un usuario nuevo y muestra todos los usuarios en una tabla con printf: nombre de usuario, nombres y apellidos en columnas.
  2. Busca a un usuario por su RUT. Como el RUT no es la clave, vas a tener que recorrer. ¿Cuántas vueltas das en el peor caso?
  3. Ahora arma un segundo diccionario, rut => nombre_usuario, y úsalo para buscar por RUT sin recorrer. ¿Qué ganaste y qué tuviste que pagar a cambio?
<?php
declare(strict_types=1);

$base_usuarios = [
    "jcbodoque" => ["nombres" => "Juan Carlos", "apellidos" => "Bodoque Bodoque", "rut" => "15.432.876-9"],
    "chernobog" => ["nombres" => "Andrés", "apellidos" => "Palma Cerna", "rut" => "20.111.222-2"],
    "sussynot32" => ["nombres" => "Susana Nicole", "apellidos" => "Jiménez Caro", "rut" => "9.876.543-3"],
];

// Escribe tu solución aquí y presiona ▶ Ejecutar


Sigue con Diseñar algoritmos.