PHP的array_map包括键

有没有办法做这样的事情:

$test_array = array("first_key" => "first_value", "second_key" => "second_value"); var_dump(array_map(function($a, $b) { return "$a loves $b"; }, array_keys($test_array), array_values($test_array))); 

但是,而不是调用array_keysarray_values ,直接传递$test_arrayvariables?

期望的输出是:

 array(2) { [0]=> string(27) "first_key loves first_value" [1]=> string(29) "second_key loves second_value" } 

不与array_map,因为它不处理键。

array_walk可以:

 $test_array = array("first_key" => "first_value", "second_key" => "second_value"); array_walk($test_array, function(&$a, $b) { $a = "$b loves $a"; }); var_dump($test_array); 

它确实改变了作为参数给出的数组,所以它不是完全的函数式编程(因为你有这样的标签问题)。

如果你愿意,你可以自己写一个这样的函数。

这可能是最简单和最简单的理由:

 $states = array('az' => 'Arizona', 'al' => 'Alabama'); array_map(function ($short, $long) { return array( 'short' => $short, 'long' => $long ); }, array_keys($states), $states); // produces: array( array('short' => 'az', 'long' => 'Arizona'), array('short' => 'al', 'long' => 'Alabama') ) 

使用PHP5.3或更高版本:

 $test_array = array("first_key" => "first_value", "second_key" => "second_value"); var_dump( array_map( function($key) use ($test_array) { return "$key loves ${test_array[$key]}"; }, array_keys($test_array) ) ); 

这是我非常简单的PHP 5.5兼容解决scheme:

 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" } 

哗众取宠

如果你需要用不同的数组多次使用函数,但使用相同的映射函数,你可以做一些叫做“ currying ”的东西,它允许你在调用时只传入数据数组:

 function array_map_assoc_curried(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_curried($func); var_dump($my_mapping($ordinals)); 

哪一个产生相同的输出,给定$func$ordinals和前面一样。

注:如果您映射的函数返回两个不同的input相同的键 ,与较晚的键关联的值将赢得。 反转input数组并输出array_map_assoc结果以允许更早的键获胜。 (在我的示例中返回的键不能碰撞,因为它们包含源数组的键,而这个键又必须是唯一的。)


替代

以下是上述的一个变种,这可能会certificate更符合逻辑,但需要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 。 如前所述,返回一个重复的键将导致以后的值获胜。

如果您使用的是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

输出和以前一样,每个都可以像以前一样进行咖喱。


概要

原始问题的目标是使调用的调用尽可能简单,代价是调用更复杂的函数; 特别是有能力将数据数组作为单个parameter passing,而不用分割键和值。 使用此答案开始处提供的function:

 $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被调用到一个高阶函数的地方,这可能是足够好的。

基于eis的回答 ,下面是我最终做的,以避免搞乱原始数组:

 $test_array = array("first_key" => "first_value", "second_key" => "second_value"); $result_array = array(); array_walk($test_array, function($a, $b) use (&$result_array) { $result_array[] = "$b loves $a"; }, $result_array); var_dump($result_array); 

通过“手动循环”我的意思是写一个使用foreach的自定义函数。 这会返回一个像array_map这样的新数组,因为函数的作用域会导致$array成为副本而不是引用:

 function map($array, callable $fn) { foreach ($array as $k => &$v) $v = call_user_func($fn, $k, $v); return $array; } 

虽然实际上使用array_maparray_map技巧似乎更简单,而且function更强大,因为您可以使用null作为callback来返回键值对:

 function map($array, callable $fn = null) { return array_map($fn, array_keys($array), $array); } 

YaLinqo库*非常适合这类任务。 它是.NET中的一个LINQ端口,完全支持所有callback中的值和键,类似于SQL。 例如:

 $mapped_array = from($test_array) ->select(function ($v, $k) { return "$k loves $v"; }) ->toArray(); 

要不就:

 $mapped_iterator = from($test_array)->select('"$k loves $v"'); 

在这里, '"$k loves $v"'是这个库支持的全闭合语法的一个快捷方式。 toArray()到底是可选的。 该方法链返回一个迭代器,所以如果结果只是需要使用foreach迭代, toArray调用可以被删除。

*由我开发

我根据eis的回答做了这个function:

 function array_map_($callback, $arr) { if (!is_callable($callback)) return $arr; $result = array_walk($arr, function(&$value, $key) use ($callback) { $value = call_user_func($callback, $key, $value); }); if (!$result) return false; return $arr; } 

例:

 $test_array = array("first_key" => "first_value", "second_key" => "second_value"); var_dump(array_map_(function($key, $value){ return $key . " loves " . $value; }, $arr)); 

输出:

 array ( 'first_key' => 'first_key loves first_value, 'second_key' => 'second_key loves second_value', ) 

当然,你可以使用array_values来返回OP所需要的。

 array_values(array_map_(function($key, $value){ return $key . " loves " . $value; }, $test_array)) 

我看到它缺less明显的答案:

 function array_map_assoc(){ if(func_num_args() < 2) throw new \BadFuncionCallException('Missing parameters'); $args = func_get_args(); $callback = $args[0]; if(!is_callable($callback)) throw new \InvalidArgumentException('First parameter musst be callable'); $arrays = array_slice($args, 1); array_walk($arrays, function(&$a){ $a = (array)$a; reset($a); }); $results = array(); $max_length = max(array_map('count', $arrays)); $arrays = array_map(function($pole) use ($max_length){ return array_pad($pole, $max_length, null); }, $arrays); for($i=0; $i < $max_length; $i++){ $elements = array(); foreach($arrays as &$v){ $elements[] = each($v); } unset($v); $out = call_user_func_array($callback, $elements); if($out === null) continue; $val = isset($out[1]) ? $out[1] : null; if(isset($out[0])){ $results[$out[0]] = $val; }else{ $results[] = $val; } } return $results; } 

和array_map完全一样。 几乎。

其实,这不是你从其他语言知道的纯粹的map 。 Php非常怪异,所以它需要一些非常怪异的用户function,因为我们不想破解我们正好破坏worse is better方法。

真的,这根本不是真正的map 。 但是,它仍然非常有用。

  • 与array_map的第一个明显区别是,callback从每个input数组中取出each()输出,而不是单独的值。 您仍然可以一次遍历更多的数组。

  • 第二个不同之处在于从callback返回之后处理密钥的方式; callback函数的返回值应该是array('new_key', 'new_value') 。 如果同一个键被返回,键可以改变,相同的键甚至可以导致以前的值被覆盖。 这不是常见的map行为,但它允许您重写密钥。

  • 第三个奇怪的是,如果你省略了返回值的key (通过array(1 => 'value')array(null, 'value') ),新键将被分配,就像$array[] = $value被使用。 这也不是map的常见行为,但我猜,有时候它很方便。

  • 第四个奇怪的是,如果callback函数没有返回值,或者返回null ,则整个当前键和值的集合在输出中被省略,只是被忽略。 这个function是完全不map py的,但是如果有这样的function的话,它会使这个function成为array_filter_assoc优秀特技。

  • 如果您在callback的返回中省略第二个元素( 1 => ... )( 部分),则使用null而不是实际值。

  • 除callback函数返回键01之外的其他元素都被忽略。

  • 最后,如果lambda返回除null或数组以外的任何值,则将其视为键和值都被省略,因此:

    1. 元素的新密钥被分配
    2. null用作它的值

警告:
请记住,这最后一个function只是以前的function的残留,它可能是完全无用的。 依靠这个function是非常不鼓励的,因为这个function将在随后的发行版中被随意的弃用和改变。

注意:
array_map不同的是,除了第一个callback参数之外,传递给array_map_assoc所有非数组参数都静默地转换为数组。

例子:
// TODO: examples, anyone?