Aqui está minha solução muito simples e compatível com PHP 5.5:
function array_map_assoc(callable $f, array $a) {
return array_column(array_map($f, array_keys($a), $a), 1, 0);
}
A chamada que você fornece deve retornar uma matriz com dois valores, ou seja return [key, value]
. A chamada interna para, array_map
portanto, produz uma matriz de matrizes. Isso é convertido novamente em uma matriz de dimensão única 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));
Resultado
array(3) {
["new first"]=>
string(7) "new 1st"
["new second"]=>
string(7) "new 2nd"
["new third"]=>
string(7) "new 3rd"
}
Aplicação parcial
Caso você precise usar a função várias vezes com matrizes diferentes, mas com a mesma função de mapeamento, é possível fazer algo chamado aplicativo de função parcial (relacionado a ' currying '), que permite transmitir apenas o array de dados após a chamada:
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));
Que produz a mesma saída, dada $func
e $ordinals
é a anterior.
NOTA: se a sua função mapeada retornar a mesma tecla para duas entradas diferentes, o valor associado à tecla posterior ganhará. Inverta a matriz de entrada e o resultado da saída array_map_assoc
para permitir que as chaves anteriores sejam vencidas. (As chaves retornadas no meu exemplo não podem colidir porque incorporam a chave da matriz de origem, que por sua vez deve ser única.)
Alternativo
A seguir está uma variante do acima, que pode ser mais lógico para alguns, mas requer o PHP 5.6:
function array_map_assoc(callable $f, array $a) {
return array_merge(...array_map($f, array_keys($a), $a));
}
Nesta variante, sua função fornecida (sobre a qual a matriz de dados é mapeada) deve retornar uma matriz associativa com uma linha, ou seja return [key => value]
. O resultado do mapeamento da chamada é simplesmente descompactado e passado para array_merge
. Como antes, o retorno de uma chave duplicada resultará na conquista de valores posteriores.
Nota: Alex83690 observou em um comentário que o uso array_replace
aqui no lugar de array_merge
preservaria chaves inteiras. array_replace
não modifica a matriz de entrada, portanto, é seguro para código funcional.
Se você estiver no PHP 5.3 a 5.5, o seguinte é equivalente. Ele usa array_reduce
e o +
operador de matriz binária para converter a matriz bidimensional resultante em uma matriz unidimensional, preservando as chaves:
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 as variantes seriam usadas assim:
$ordinals = [
'first' => '1st',
'second' => '2nd',
'third' => '3rd',
];
$func = function ($k, $v) {
return ['new ' . $k => 'new ' . $v];
};
var_dump(array_map_assoc($func, $ordinals));
Observe o em =>
vez de ,
em $func
.
A saída é a mesma de antes e cada uma pode ser parcialmente aplicada da mesma maneira que antes.
Resumo
O objetivo da pergunta original é tornar a chamada a mais simples possível, às custas de ter uma função mais complicada que é chamada; especialmente, ter a capacidade de transmitir a matriz de dados como um único argumento, sem dividir as chaves e os valores. Usando a função fornecida no início desta resposta:
$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)));
Ou, apenas para esta pergunta, podemos simplificar a array_map_assoc()
função que descarta as teclas de saída, pois a pergunta não as pede:
$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));
Portanto, a resposta é NÃO , você não pode evitar ligar array_keys
, mas pode abstrair o local em que array_keys
é chamado para uma função de ordem superior, o que pode ser bom o suficiente.