有没有办法做这样的事情:
$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
变量?
所需的输出是:
array(2) {
[0]=>
string(27) "first_key loves first_value"
[1]=>
string(29) "second_key loves second_value"
}
答案 0 :(得分:157)
不使用array_map,因为它不处理密钥。
$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);
// array(2) {
// ["first_key"]=>
// string(27) "first_key loves first_value"
// ["second_key"]=>
// string(29) "second_key loves second_value"
// }
它确实更改了作为参数给出的数组,因此它不是完全函数式编程(因为你有像这样标记的问题)。另外,正如注释中所指出的,这只会更改数组的值,因此键不会是您在问题中指定的键。
如果您愿意,可以编写一个修复上述点的函数,如下所示:
function mymapper($arrayparam, $valuecallback) {
$resultarr = array();
foreach ($arrayparam as $key => $value) {
$resultarr[] = $valuecallback($key, $value);
}
return $resultarr;
}
$test_array = array("first_key" => "first_value",
"second_key" => "second_value");
$new_array = mymapper($test_array, function($a, $b) { return "$a loves $b"; });
var_dump($new_array);
// array(2) {
// [0]=>
// string(27) "first_key loves first_value"
// [1]=>
// string(29) "second_key loves second_value"
// }
答案 1 :(得分:114)
这可能是最短且最容易推理的:
$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')
)
答案 2 :(得分:51)
这是我非常简单的PHP 5.5兼容解决方案:
function array_map_assoc(callable $f, array $a) {
return array_column(array_map($f, array_keys($a), $a), 1, 0);
}
您提供的callable本身应返回一个包含两个值的数组,即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"
}
如果你需要多次使用不同数组但具有相同映射功能的函数,你可以做一些名为partial function application(与'currying'相关)的事情,它允许你只通过在调用时在数据数组中:
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
将保留整数键。array_reduce
不会修改输入数组,因此对于功能代码是安全的。
如果您使用的是PHP 5.3到5.5,则以下内容是等效的。它使用+
和二进制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
所以答案是 NO ,你无法避免调用array_keys
,但你可以抽象出DELIMITER //
CREATE PROCEDURE SP_LOGIN(IN ID VARCHAR(50),IN PASSWORD VARCHAR(50) )
BEGIN
SELECT * FROM USERS WHERE ID=ID AND PASSWORD=PASSWORD;
END //
DELIMITER ;
被调用到更高阶函数的地方,这可能足够好了。
答案 3 :(得分:20)
使用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)
)
);
答案 4 :(得分:7)
$array = [
'category1' => 'first category',
'category2' => 'second category',
];
$new = array_map(function($key, $value) {
return "{$key} => {$value}";
}, array_keys($array), $array);
答案 5 :(得分:4)
这就是我在项目中实现这一点的方式。
function array_map_associative(callable $callback, $array) {
/* map original array keys, and call $callable with $key and value of $key from original array. */
return array_map(function($key) use ($callback, $array){
return $callback($key, $array[$key]);
}, array_keys($array));
}
答案 6 :(得分:2)
基于eis's answer,这是我最终做的,以避免弄乱原始数组:
$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);
答案 7 :(得分:2)
通过“手动循环”我的意思是编写一个使用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_keys
的技术实际上看起来更简单,更强大,因为您可以使用null
作为回调来返回键值对:
function map($array, callable $fn = null) {
return array_map($fn, array_keys($array), $array);
}
答案 8 :(得分:2)
YaLinqo库*非常适合这类任务。它是.NET的LINQ端口,完全支持所有回调中的值和键,类似于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
调用。
*由我开发
答案 9 :(得分:1)
我根据eis's answer:
制作了这个功能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))
答案 10 :(得分:1)
我会做这样的事情:
<?php
/**
* array_map_kv()
* An array mapping function to map with both keys and values.
*
* @param $callback callable
* A callback function($key, $value) for mapping values.
* @param $array array
* An array for mapping.
*/
function array_map_kv(callable $callback, array $array) {
return array_map(
function ($key) use ($callback, $array) {
return $callback($key, $array[$key]); // $callback($key, $value)
},
array_keys($array)
);
}
// use it
var_dump(array_map_kv(function ($key, $value) {
return "{$key} loves {$value}";
}, array(
"first_key" => "first_value",
"second_key" => "second_value",
)));
?>
结果:
array(2) {
[0]=>
string(27) "first_key loves first_value"
[1]=>
string(29) "second_key loves second_value"
}
答案 11 :(得分:1)
看这里!有一个简单的解决方案!
function array_map2(callable $f, array $a)
{
return array_map($f, array_keys($a), $a);
}
如问题中所述,array_map
已经完全具备所需的功能。这里的其他答案严重使事情复杂化:array_walk
无法正常工作。
用法
与您的示例完全相同:
$test_array = array("first_key" => "first_value",
"second_key" => "second_value");
var_dump(array_map2(function($a, $b) { return "$a loves $b"; }, $test_array));
答案 12 :(得分:1)
我将使用5.6或更高版本为该问题添加另一种解决方案。不知道它是否比已经很好的解决方案更有效(可能没有),但是对我来说,它更容易阅读:
$myArray = [
"key0" => 0,
"key1" => 1,
"key2" => 2
];
array_combine(
array_keys($myArray),
array_map(
function ($intVal) {
return strval($intVal);
},
$myArray
)
);
使用strval()
作为array_map
中的示例函数,它将生成:
array(3) {
["key0"]=>
string(1) "0"
["key1"]=>
string(1) "1"
["key2"]=>
string(1) "2"
}
希望我不是唯一一个发现这一点很容易理解的人。
array_combine
从键数组和值数组创建一个key => value
数组,其余内容很容易解释。
答案 13 :(得分:0)
如果您只需要一次closure,则它会起作用。我会使用generator。
$test_array = [
"first_key" => "first_value",
"second_key" => "second_value",
];
$x_result = (function(array $arr) {
foreach ($arr as $key => $value) {
yield "$key loves $value";
}
})($test_array);
var_dump(iterator_to_array($x_result));
// array(2) {
// [0]=>
// string(27) "first_key loves first_value"
// [1]=>
// string(29) "second_key loves second_value"
// }
关于可重复使用的内容:
function xmap(callable $cb, array $arr)
{
foreach ($arr as $key => $value) {
yield $cb($key, $value);
}
}
var_dump(iterator_to_array(
xmap(function($a, $b) { return "$a loves $b"; }, $test_array)
));
答案 14 :(得分:0)
您可以使用此map中的array library方法来轻松实现所需的精确目标,
Arr::map($test_array, function($a, $b) { return "$a loves $b"; });
它还保留键并返回新数组,更不用说几种不同的模式来满足您的需求。
答案 15 :(得分:0)
(没有)保留密钥的另一种方法:
$test_array = [
"first_key" => "first_value",
"second_key" => "second_value"
];
$f = function($ar) {
return array_map(
function($key, $val) {
return "{$key} - {$val}";
},
array_keys($ar),
$ar
);
};
#-- WITHOUT preserving keys
$res = $f($test_array);
#-- WITH preserving keys
$res = array_combine(
array_keys($test_array),
$f($test_array)
);
答案 16 :(得分:0)
我总是喜欢数组映射的javascript变种。最简单的版本是:
/**
* @param array $array
* @param callable $callback
* @return array
*/
function arrayMap(array $array, callable $callback)
{
$newArray = [];
foreach( $array as $key => $value )
{
$newArray[] = call_user_func($callback, $value, $key, $array);
}
return $newArray;
}
所以现在你可以传递一个如何构造值的回调函数。
$testArray = [
"first_key" => "first_value",
"second_key" => "second_value"
];
var_dump(
arrayMap($testArray, function($value, $key) {
return $key . ' loves ' . $value;
});
);
答案 17 :(得分:0)
我看到它错过了明显的答案:
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非常奇怪,所以它需要一些非常奇怪的用户函数,因为我们不想破坏我们精确损坏的worse is better
方法。
真的,实际上根本不是map
。然而,它仍然非常有用。
与array_map的第一个明显区别是,回调从每个输入数组中获取each()
的输出而不是单独的值。您仍然可以一次迭代更多数组。
第二个区别是密钥在回调后的处理方式;回调函数的返回值应为array('new_key', 'new_value')
。密钥可以并且将被更改,如果返回相同的密钥,则相同的密钥甚至可能导致先前的值被覆盖。这不是常见的map
行为,但它允许您重写密钥。
第三个奇怪的是,如果您在返回值中忽略key
(array(1 => 'value')
或array(null, 'value')
),则会分配新密钥,就好像{{ 1}}被使用了。这也不是$array[] = $value
的常见行为,但有时它会很方便。
第四个奇怪的是,如果回调函数没有返回值,或者返回map
,则输出中会省略整组当前键和值,只是跳过它。这个功能完全不是null
py,但如果有这样的功能,它会使map
的这个功能成为极好的特技双倍。
如果在回调的返回中省略第二个元素(array_filter_assoc
)(值部分),则使用1 => ...
而不是实际值。
忽略回调返回中除null
和0
键之外的任何其他元素。
最后,如果lambda返回除1
或数组之外的任何值,则将其视为省略了键和值,因此:
null
用作其值警告:
请记住,这最后一个功能只是以前功能的残留,它可能完全没用。非常不鼓励依赖此功能,因为此功能将随机已弃用并在将来的版本中意外更改。
注意:
与null
不同,传递给array_map
的所有非数组参数(第一个回调参数除外)都以静默方式转换为数组。
实施例:
array_map_assoc