Aquí está mi solución muy simple, compatible con PHP 5.5:
function array_map_assoc(callable $f, array $a) {
return array_column(array_map($f, array_keys($a), $a), 1, 0);
}
El invocable que proporcione debería devolver una matriz con dos valores, es decir return [key, value]
. La llamada interna a, array_map
por lo tanto, produce una matriz de matrices. Esto se convierte de nuevo en una matriz de una sola dimensión por array_column
.
Uso
$ordinals = [
'first' => '1st',
'second' => '2nd',
'third' => '3rd',
];
$func = function ($k, $v) {
return ['new ' . $k, 'new ' . $v];
};
var_dump(array_map_assoc($func, $ordinals));
Salida
array(3) {
["new first"]=>
string(7) "new 1st"
["new second"]=>
string(7) "new 2nd"
["new third"]=>
string(7) "new 3rd"
}
Aplicación parcial
En caso de que necesite usar la función muchas veces con diferentes matrices pero con la misma función de mapeo, puede hacer algo llamado aplicación de función parcial (relacionada con ' curry '), que le permite pasar solo la matriz de datos tras la invocación:
function array_map_assoc_partial(callable $f) {
return function (array $a) use ($f) {
return array_column(array_map($f, array_keys($a), $a), 1, 0);
};
}
...
$my_mapping = array_map_assoc_partial($func);
var_dump($my_mapping($ordinals));
Lo que produce el mismo resultado, dado $func
y $ordinals
son como antes.
NOTA: si su función asignada devuelve la misma tecla para dos entradas diferentes, el valor asociado con la tecla posterior ganará. Invierta la matriz de entrada y el resultado de salida array_map_assoc
para permitir que las teclas anteriores ganen. (Las claves devueltas en mi ejemplo no pueden colisionar ya que incorporan la clave de la matriz fuente, que a su vez debe ser única).
Alternativa
La siguiente es una variante de lo anterior, que puede resultar más lógico para algunos, pero requiere PHP 5.6:
function array_map_assoc(callable $f, array $a) {
return array_merge(...array_map($f, array_keys($a), $a));
}
En esta variante, su función suministrada (sobre la cual se asigna la matriz de datos) debería devolver una matriz asociativa con una fila, es decir return [key => value]
. El resultado de mapear el invocable simplemente se desempaqueta y se pasa a array_merge
. Como antes, devolver una clave duplicada dará como resultado valores posteriores ganadores.
nb Alex83690 ha notado en un comentario que usar array_replace
aquí en lugar de array_merge
preservaría las claves de enteros. array_replace
no modifica la matriz de entrada, por lo que es seguro para el código funcional.
Si está en PHP 5.3 a 5.5, lo siguiente es equivalente. Utiliza array_reduce
y el +
operador de matriz binaria para convertir la matriz bidimensional resultante en una matriz unidimensional mientras conserva las claves:
function array_map_assoc(callable $f, array $a) {
return array_reduce(array_map($f, array_keys($a), $a), function (array $acc, array $a) {
return $acc + $a;
}, []);
}
Uso
Ambas variantes se usarían así:
$ordinals = [
'first' => '1st',
'second' => '2nd',
'third' => '3rd',
];
$func = function ($k, $v) {
return ['new ' . $k => 'new ' . $v];
};
var_dump(array_map_assoc($func, $ordinals));
Tenga =>
en cuenta el en lugar de ,
en $func
.
El resultado es el mismo que antes, y cada uno se puede aplicar parcialmente de la misma manera que antes.
Resumen
El objetivo de la pregunta original es hacer que la invocación de la llamada sea lo más simple posible, a expensas de tener una función más complicada que se invoca; especialmente, tener la capacidad de pasar la matriz de datos como un solo argumento, sin dividir las claves y los valores. Usando la función provista al comienzo de esta respuesta:
$test_array = ["first_key" => "first_value",
"second_key" => "second_value"];
$array_map_assoc = function (callable $f, array $a) {
return array_column(array_map($f, array_keys($a), $a), 1, 0);
};
$f = function ($key, $value) {
return [$key, $key . ' loves ' . $value];
};
var_dump(array_values($array_map_assoc($f, $test_array)));
O, solo para esta pregunta, podemos hacer una simplificación de la array_map_assoc()
función que elimina las teclas de salida, ya que la pregunta no las solicita:
$test_array = ["first_key" => "first_value",
"second_key" => "second_value"];
$array_map_assoc = function (callable $f, array $a) {
return array_map($f, array_keys($a), $a);
};
$f = function ($key, $value) {
return $key . ' loves ' . $value;
};
var_dump($array_map_assoc($f, $test_array));
Entonces, la respuesta es NO , no puede evitar llamar array_keys
, pero puede abstraer el lugar donde array_keys
se llama a una función de orden superior, lo que podría ser lo suficientemente bueno.