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_keys
和array_values
,直接传递$test_array
variables?
期望的输出是:
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_map
和array_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函数返回键
0
和1
之外的其他元素都被忽略。 -
最后,如果lambda返回除
null
或数组以外的任何值,则将其视为键和值都被省略,因此:- 元素的新密钥被分配
-
null
用作它的值
警告:
请记住,这最后一个function只是以前的function的残留,它可能是完全无用的。 依靠这个function是非常不鼓励的,因为这个function将在随后的发行版中被随意的弃用和改变。
注意:
与array_map
不同的是,除了第一个callback参数之外,传递给array_map_assoc
所有非数组参数都静默地转换为数组。
例子:
// TODO: examples, anyone?