Funciones de Orden Superior
Las funciones de orden superior son funciones que reciben otras funciones como argumentos o que retornan funciones. PHP incluye varias funciones de este tipo muy utiles para trabajar con arrays de forma declarativa y expresiva.
¿Que son las funciones de orden superior?
Una funcion de orden superior es una funcion que cumple al menos una de estas condiciones:
- Recibe una o más funciones como argumentos
- Retorna una funcion como resultado
<?php
declare(strict_types=1);
// Funcion de orden superior que recibe una funcion
function aplicarATodos(array $items, callable $funcion): array
{
$resultado = [];
foreach ($items as $item) {
$resultado[] = $funcion($item);
}
return $resultado;
}
// Uso
$numeros = [1, 2, 3, 4, 5];
$cuadrados = aplicarATodos($numeros, fn (int $n): int => $n ** 2);
print_r($cuadrados); // [1, 4, 9, 16, 25]
// Funcion de orden superior que retorna una funcion
function crearSumador(int $valor): callable
{
return fn (int $n): int => $n + $valor;
}
$sumar10 = crearSumador(10);
echo $sumar10(5); // 15
echo $sumar10(20); // 30
array_map: Transformar elementos
array_map aplica una funcion a cada elemento de un array
y retorna un nuevo array con los resultados:
<?php
declare(strict_types=1);
$numeros = [1, 2, 3, 4, 5];
// Duplicar cada numero
$dobles = array_map(fn (int $n): int => $n * 2, $numeros);
print_r($dobles); // [2, 4, 6, 8, 10]
// Convertir a strings
$strings = array_map(fn (int $n): string => "Numero: $n", $numeros);
print_r($strings); // ['Numero: 1', 'Numero: 2', ...]
// Con arrays asociativos
$usuarios = [
['nombre' => 'Ana', 'email' => 'ana@example.com'],
['nombre' => 'Luis', 'email' => 'luis@example.com'],
];
$emails = array_map(fn (array $u): string => $u['email'], $usuarios);
print_r($emails); // ['ana@example.com', 'luis@example.com']
// Transformar estructura
$usuariosFormateados = array_map(
fn (array $u): string => "{$u['nombre']} <{$u['email']}>",
$usuarios
);
print_r($usuariosFormateados);
// ['Ana ', 'Luis ']
array_map con multiples arrays
<?php
declare(strict_types=1);
$nombres = ['Ana', 'Luis', 'Eva'];
$edades = [25, 30, 28];
// Combinar dos arrays elemento a elemento
$personas = array_map(
fn (string $nombre, int $edad): array => ['nombre' => $nombre, 'edad' => $edad],
$nombres,
$edades
);
print_r($personas);
/*
[
['nombre' => 'Ana', 'edad' => 25],
['nombre' => 'Luis', 'edad' => 30],
['nombre' => 'Eva', 'edad' => 28],
]
*/
// Sumar elementos de dos arrays
$a = [1, 2, 3];
$b = [10, 20, 30];
$sumas = array_map(fn (int $x, int $y): int => $x + $y, $a, $b);
print_r($sumas); // [11, 22, 33]
array_filter: Filtrar elementos
array_filter retorna un nuevo array con los elementos que
pasan una condicion:
<?php
declare(strict_types=1);
$numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Filtrar pares
$pares = array_filter($numeros, fn (int $n): bool => $n % 2 === 0);
print_r($pares); // [1 => 2, 3 => 4, 5 => 6, 7 => 8, 9 => 10]
// Nota: array_filter preserva las claves originales
// Usa array_values si necesitas reindexar
$paresReindexados = array_values($pares);
print_r($paresReindexados); // [2, 4, 6, 8, 10]
// Filtrar mayores que 5
$mayores = array_filter($numeros, fn (int $n): bool => $n > 5);
print_r(array_values($mayores)); // [6, 7, 8, 9, 10]
// Sin callback: elimina valores falsy (0, '', null, false)
$mixto = [0, 1, '', 'hola', null, true, false];
$sinFalsy = array_filter($mixto);
print_r($sinFalsy); // [1 => 1, 3 => 'hola', 5 => true]
Filtrar con acceso a claves
<?php
declare(strict_types=1);
$datos = [
'nombre' => 'Ana',
'edad' => 25,
'email' => 'ana@example.com',
'password' => 'secreto123',
'telefono' => '555-1234',
];
// Filtrar por clave: ARRAY_FILTER_USE_KEY
$soloNombreEmail = array_filter(
$datos,
fn (string $clave): bool => in_array($clave, ['nombre', 'email'], true),
ARRAY_FILTER_USE_KEY
);
print_r($soloNombreEmail);
// ['nombre' => 'Ana', 'email' => 'ana@example.com']
// Filtrar por clave y valor: ARRAY_FILTER_USE_BOTH
$filtrado = array_filter(
$datos,
fn (mixed $valor, string $clave): bool =>
$clave !== 'password' && $valor !== '',
ARRAY_FILTER_USE_BOTH
);
print_r($filtrado);
array_reduce: Reducir a un valor
array_reduce reduce un array a un solo valor aplicando
una funcion acumuladora:
<?php
declare(strict_types=1);
$numeros = [1, 2, 3, 4, 5];
// Sumar todos los elementos
$suma = array_reduce(
$numeros,
fn (int $acumulador, int $actual): int => $acumulador + $actual,
0 // valor inicial
);
echo $suma; // 15
// Multiplicar todos los elementos
$producto = array_reduce(
$numeros,
fn (int $acc, int $n): int => $acc * $n,
1 // valor inicial (1 para multiplicacion)
);
echo $producto; // 120
// Encontrar el maximo
$maximo = array_reduce(
$numeros,
fn (int $acc, int $n): int => $n > $acc ? $n : $acc,
PHP_INT_MIN
);
echo $maximo; // 5
// Concatenar strings
$palabras = ['Hola', 'mundo', 'PHP'];
$frase = array_reduce(
$palabras,
fn (string $acc, string $palabra): string =>
$acc === '' ? $palabra : "$acc $palabra",
''
);
echo $frase; // 'Hola mundo PHP'
Casos avanzados de reduce
<?php
declare(strict_types=1);
$productos = [
['nombre' => 'Laptop', 'precio' => 999, 'cantidad' => 1],
['nombre' => 'Mouse', 'precio' => 25, 'cantidad' => 2],
['nombre' => 'Teclado', 'precio' => 75, 'cantidad' => 1],
];
// Calcular total del carrito
$total = array_reduce(
$productos,
fn (float $acc, array $p): float => $acc + ($p['precio'] * $p['cantidad']),
0.0
);
echo "Total: $total"; // Total: 1124
// Agrupar por categoria
$items = [
['tipo' => 'fruta', 'nombre' => 'manzana'],
['tipo' => 'verdura', 'nombre' => 'zanahoria'],
['tipo' => 'fruta', 'nombre' => 'pera'],
['tipo' => 'verdura', 'nombre' => 'brocoli'],
];
$agrupados = array_reduce(
$items,
function (array $acc, array $item): array {
$tipo = $item['tipo'];
if (!isset($acc[$tipo])) {
$acc[$tipo] = [];
}
$acc[$tipo][] = $item['nombre'];
return $acc;
},
[]
);
print_r($agrupados);
/*
[
'fruta' => ['manzana', 'pera'],
'verdura' => ['zanahoria', 'brocoli'],
]
*/
// Contar ocurrencias
$letras = ['a', 'b', 'a', 'c', 'b', 'a'];
$conteo = array_reduce(
$letras,
function (array $acc, string $letra): array {
$acc[$letra] = ($acc[$letra] ?? 0) + 1;
return $acc;
},
[]
);
print_r($conteo); // ['a' => 3, 'b' => 2, 'c' => 1]
usort, uasort, uksort: Ordenar con funciones
Estas funciones permiten ordenar arrays usando una funcion de comparacion personalizada:
<?php
declare(strict_types=1);
// usort: ordena por valores, reindexa
$usuarios = [
['nombre' => 'Ana', 'edad' => 28],
['nombre' => 'Luis', 'edad' => 35],
['nombre' => 'Eva', 'edad' => 22],
];
// Ordenar por edad ascendente
usort($usuarios, fn (array $a, array $b): int => $a['edad'] <=> $b['edad']);
// Eva(22), Ana(28), Luis(35)
// Ordenar por edad descendente
usort($usuarios, fn (array $a, array $b): int => $b['edad'] <=> $a['edad']);
// Luis(35), Ana(28), Eva(22)
// uasort: ordena por valores, preserva claves
$puntuaciones = [
'jugador1' => 150,
'jugador2' => 200,
'jugador3' => 100,
];
uasort($puntuaciones, fn (int $a, int $b): int => $b <=> $a);
print_r($puntuaciones);
// ['jugador2' => 200, 'jugador1' => 150, 'jugador3' => 100]
// uksort: ordena por claves
$datos = [
'zebra' => 1,
'alfa' => 2,
'beta' => 3,
];
uksort($datos, fn (string $a, string $b): int => $a <=> $b);
print_r($datos); // ['alfa' => 2, 'beta' => 3, 'zebra' => 1]
array_walk: Modificar elementos
array_walk aplica una funcion a cada elemento del array
original, modificandolo en lugar:
<?php
declare(strict_types=1);
$precios = [100, 200, 300];
// Aplicar descuento del 10% a cada precio
// Nota: el primer parametro se pasa por referencia
array_walk($precios, function (int|float &$precio): void {
$precio = $precio * 0.9;
});
print_r($precios); // [90, 180, 270]
// Con clave disponible
$productos = ['laptop' => 999, 'mouse' => 25, 'teclado' => 75];
array_walk($productos, function (int &$precio, string $nombre): void {
echo "$nombre: \$$precio\n";
$precio = (int) ($precio * 1.21); // Añadir IVA
});
print_r($productos); // ['laptop' => 1208, 'mouse' => 30, 'teclado' => 90]
// Con dato adicional
$valores = [10, 20, 30];
$factor = 5;
array_walk($valores, function (int &$valor, int $key, int $factor): void {
$valor = $valor * $factor;
}, $factor);
print_r($valores); // [50, 100, 150]
array_map retorna un nuevo array y no modifica el original.
array_walk modifica el array original y retorna true/false.
Combinar funciones
El verdadero poder de las funciones de orden superior esta en combinarlas:
<?php
declare(strict_types=1);
$pedidos = [
['cliente' => 'Ana', 'total' => 150, 'estado' => 'completado'],
['cliente' => 'Luis', 'total' => 50, 'estado' => 'pendiente'],
['cliente' => 'Eva', 'total' => 200, 'estado' => 'completado'],
['cliente' => 'Carlos', 'total' => 75, 'estado' => 'cancelado'],
['cliente' => 'Maria', 'total' => 300, 'estado' => 'completado'],
];
// Objetivo: suma de pedidos completados mayores a 100
// Paso 1: Filtrar completados
$completados = array_filter(
$pedidos,
fn (array $p): bool => $p['estado'] === 'completado'
);
// Paso 2: Filtrar mayores a 100
$mayores100 = array_filter(
$completados,
fn (array $p): bool => $p['total'] > 100
);
// Paso 3: Extraer totales
$totales = array_map(fn (array $p): int => $p['total'], $mayores100);
// Paso 4: Sumar
$suma = array_sum($totales);
echo "Total: $suma"; // 650
// Version compacta (encadenada)
$sumaCompacta = array_sum(
array_map(
fn (array $p): int => $p['total'],
array_filter(
$pedidos,
fn (array $p): bool =>
$p['estado'] === 'completado' && $p['total'] > 100
)
)
);
echo "Total: $sumaCompacta"; // 650
Crear tus propias funciones de orden superior
<?php
declare(strict_types=1);
// Funcion que encuentra el primer elemento que cumple condicion
function encontrarPrimero(array $items, callable $condicion): mixed
{
foreach ($items as $item) {
if ($condicion($item)) {
return $item;
}
}
return null;
}
$usuarios = [
['id' => 1, 'nombre' => 'Ana', 'activo' => false],
['id' => 2, 'nombre' => 'Luis', 'activo' => true],
['id' => 3, 'nombre' => 'Eva', 'activo' => true],
];
$primerActivo = encontrarPrimero(
$usuarios,
fn (array $u): bool => $u['activo']
);
print_r($primerActivo); // ['id' => 2, 'nombre' => 'Luis', ...]
// Funcion que verifica si todos los elementos cumplen condicion
function todos(array $items, callable $condicion): bool
{
foreach ($items as $item) {
if (!$condicion($item)) {
return false;
}
}
return true;
}
// Funcion que verifica si algun elemento cumple condicion
function alguno(array $items, callable $condicion): bool
{
foreach ($items as $item) {
if ($condicion($item)) {
return true;
}
}
return false;
}
$numeros = [2, 4, 6, 8];
var_dump(todos($numeros, fn (int $n): bool => $n % 2 === 0)); // true
var_dump(todos($numeros, fn (int $n): bool => $n > 5)); // false
var_dump(alguno($numeros, fn (int $n): bool => $n > 5)); // true
Ejemplo practico: Pipeline de procesamiento
<?php
declare(strict_types=1);
// Funcion pipe que ejecuta funciones en secuencia
function pipe(mixed $valor, callable ...$funciones): mixed
{
return array_reduce(
$funciones,
fn (mixed $acc, callable $fn): mixed => $fn($acc),
$valor
);
}
// Transformadores individuales
$trimear = fn (string $s): string => trim($s);
$minusculas = fn (string $s): string => strtolower($s);
$sinEspaciosDobles = fn (string $s): string => preg_replace('/\s+/', ' ', $s) ?? $s;
$capitalizar = fn (string $s): string => ucwords($s);
// Usar el pipeline
$entrada = ' HOLA MUNDO PHP ';
$resultado = pipe(
$entrada,
$trimear,
$minusculas,
$sinEspaciosDobles,
$capitalizar
);
echo $resultado; // 'Hola Mundo Php'
// Pipeline para numeros
$duplicar = fn (int $n): int => $n * 2;
$sumar10 = fn (int $n): int => $n + 10;
$alCuadrado = fn (int $n): int => $n ** 2;
$numero = pipe(5, $duplicar, $sumar10, $alCuadrado);
echo $numero; // 400 ((5 * 2 + 10) ^ 2)
Ejemplo practico: Procesador de datos
<?php
declare(strict_types=1);
// Datos de ventas
$ventas = [
['fecha' => '2024-01-15', 'producto' => 'A', 'cantidad' => 10, 'precio' => 25.00],
['fecha' => '2024-01-15', 'producto' => 'B', 'cantidad' => 5, 'precio' => 50.00],
['fecha' => '2024-01-16', 'producto' => 'A', 'cantidad' => 8, 'precio' => 25.00],
['fecha' => '2024-01-16', 'producto' => 'C', 'cantidad' => 3, 'precio' => 100.00],
['fecha' => '2024-01-17', 'producto' => 'B', 'cantidad' => 12, 'precio' => 50.00],
];
// Calcular total por venta
$ventasConTotal = array_map(
fn (array $v): array => [
...$v,
'total' => $v['cantidad'] * $v['precio'],
],
$ventas
);
// Filtrar ventas mayores a 200
$ventasGrandes = array_filter(
$ventasConTotal,
fn (array $v): bool => $v['total'] > 200
);
// Ordenar por total descendente
usort($ventasGrandes, fn (array $a, array $b): int => (int) ($b['total'] - $a['total']));
// Calcular estadisticas
$totalVentas = array_reduce(
$ventasConTotal,
fn (float $acc, array $v): float => $acc + $v['total'],
0.0
);
$promedioVenta = $totalVentas / count($ventasConTotal);
// Agrupar por producto
$porProducto = array_reduce(
$ventasConTotal,
function (array $acc, array $venta): array {
$producto = $venta['producto'];
if (!isset($acc[$producto])) {
$acc[$producto] = ['cantidad' => 0, 'total' => 0.0];
}
$acc[$producto]['cantidad'] += $venta['cantidad'];
$acc[$producto]['total'] += $venta['total'];
return $acc;
},
[]
);
echo "Total ventas: \$$totalVentas\n"; // Total ventas: $1750
echo "Promedio: \$$promedioVenta\n"; // Promedio: $350
print_r($porProducto);
/*
[
'A' => ['cantidad' => 18, 'total' => 450],
'B' => ['cantidad' => 17, 'total' => 850],
'C' => ['cantidad' => 3, 'total' => 300],
]
*/
Ejercicios
Ejercicio 1: Transformar con array_map
Dado un array de nombres en minusculas ['ana', 'luis', 'eva'],
usa array_map para convertirlos a mayusculas. Usa una arrow function.
Ver solucion
<?php
declare(strict_types=1);
$nombres = ['ana', 'luis', 'eva'];
$nombresMayusculas = array_map(
fn (string $nombre): string => strtoupper($nombre),
$nombres
);
print_r($nombresMayusculas);
// ['ANA', 'LUIS', 'EVA']
Ejercicio 2: Filtrar con array_filter
Tienes un array de productos:
[['nombre' => 'A', 'stock' => 5], ['nombre' => 'B', 'stock' => 0], ['nombre' => 'C', 'stock' => 12]].
Usa array_filter para quedarte solo con los productos que tienen stock mayor a 0.
Ver solucion
<?php
declare(strict_types=1);
$productos = [
['nombre' => 'A', 'stock' => 5],
['nombre' => 'B', 'stock' => 0],
['nombre' => 'C', 'stock' => 12],
];
$conStock = array_filter(
$productos,
fn (array $p): bool => $p['stock'] > 0
);
print_r(array_values($conStock));
// [['nombre' => 'A', 'stock' => 5], ['nombre' => 'C', 'stock' => 12]]
Ejercicio 3: Reducir con array_reduce
Dado un array de numeros [10, 20, 30, 40], usa array_reduce
para calcular el producto de todos los elementos (multiplicarlos entre si).
El resultado debe ser 240000.
Ver solucion
<?php
declare(strict_types=1);
$numeros = [10, 20, 30, 40];
$producto = array_reduce(
$numeros,
fn (int $acc, int $n): int => $acc * $n,
1 // valor inicial para multiplicacion
);
echo $producto; // 240000
¿Has encontrado un error o tienes una sugerencia para mejorar esta leccion?
Escribenos¿Te está gustando el curso?
Tenemos cursos premium con proyectos reales y soporte personalizado.
Descubrir cursos premium