下面是我的非常简单的、兼容PHP 5.5的解决方案:
function array_map_assoc(callable $f, array $a) {
return array_column(array_map($f, array_keys($a), $a), 1, 0);
}
你提供的可调用对象本身应该返回一个包含两个值的数组,即return [key, value]。因此,对array_map的内部调用会生成一个数组的数组。然后通过array_column将其转换回一维数组。
使用
$ordinals = [
'first' => '1st',
'second' => '2nd',
'third' => '3rd',
];
$func = function ($k, $v) {
return ['new ' . $k, 'new ' . $v];
};
var_dump(array_map_assoc($func, $ordinals));
输出
array(3) {
["new first"]=>
string(7) "new 1st"
["new second"]=>
string(7) "new 2nd"
["new third"]=>
string(7) "new 3rd"
}
部分应用程序
如果你需要多次使用不同数组但相同映射函数的函数,你可以做一些叫做部分函数应用(与' curiling '相关),它允许你在调用时只传入数据数组:
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));
它产生相同的输出,假设$func和$ordinals与前面一样。
注意:如果映射函数为两个不同的输入返回相同的键,则与后一个键关联的值将胜出。反转array_map_assoc的输入数组和输出结果,以允许较早的键获胜。(在我的例子中,返回的键不能冲突,因为它们包含了源数组的键,而源数组的键必须是唯一的。)
替代
下面是上面的一个变体,对某些人来说可能更符合逻辑,但需要PHP 5.6:
function array_map_assoc(callable $f, array $a) {
return array_merge(...array_map($f, array_keys($a), $a));
}
在这种变体中,你提供的函数(数据数组的映射)应该返回一个只有一行的关联数组,即return [key => value]。
映射可调用对象的结果然后被简单地解包并传递给array_merge。与前面一样,返回重复的键将导致后面的值胜出。
n.b. Alex83690在注释中指出,在这里使用array_replace代替array_merge将保留整型键。Array_replace不修改输入数组,因此对于函数代码是安全的。
如果您使用的是PHP 5.3到5.5,那么下面的代码是等效的。它使用array_reduce和二进制+数组操作符将生成的二维数组转换为一维数组,同时保留键:
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;
}, []);
}
使用
这两种变体的用法如下:
$ordinals = [
'first' => '1st',
'second' => '2nd',
'third' => '3rd',
];
$func = function ($k, $v) {
return ['new ' . $k => 'new ' . $v];
};
var_dump(array_map_assoc($func, $ordinals));
注意在$func中=>而不是。
输出与之前相同,并且每个都可以以与之前相同的方式部分应用。
总结
最初问题的目标是使调用的调用尽可能简单,代价是要调用一个更复杂的函数;特别是,能够将数据数组作为单个参数传递进来,而不分离键和值。使用答案开头提供的函数:
$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)));
或者,仅对于这个问题,我们可以简化为array_map_assoc()函数,该函数删除输出键,因为这个问题不需要它们:
$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));
所以答案是否定的,你不能避免调用array_keys,但是你可以把array_keys被调用的地方抽象为一个高阶函数,这就足够了。