使用array_map()访问第一级键,而不调用' array_keys() '

有没有这样做的方法:

$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_array变量?

期望的输出是:

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

我所说的“手动循环”是指编写一个使用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_keys的技术实际上看起来更简单,也更强大,因为你可以使用null作为回调来返回键-值对:

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

不是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);


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

基于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);

我发现它忽略了最明显的答案:

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()的输出,而不仅仅是value。您仍然可以一次迭代多个数组。

  • 第二个区别是键从回调返回后的处理方式;回调函数的返回值应该是array('new_key', 'new_value')。键可以并且将被更改,如果返回相同的键,相同的键甚至可以导致先前的值被覆盖。这不是常见的map行为,但它允许你重写键。

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

  • 第四个奇怪的地方是,如果回调函数没有返回值,或者返回null,整个当前键和值集将从输出中被省略,它只是被跳过。这个特性完全不是__abc1py,但是它会使这个函数成为array_filter_assoc的出色替身,如果有这样的函数的话。

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

  • 在回调函数的返回值中,除了那些带有01键的元素外,其他任何元素都将被忽略。

  • 最后,如果lambda返回除null或array之外的任何值,则会被视为键和值都被省略,因此:

    1. 为元素分配新的键值
    2. null被用作它的值
    3. 李< / ol > < / >
警告:< br > 请记住,最后一个功能只是前面功能的残留,它可能完全无用。依赖这个特性是非常不鼓励的,因为这个特性将是随机的弃用,并在未来的版本中意外地改变。
< p >注意:< br > 与array_map不同,传递给array_map_assoc的所有非数组参数,除第一个回调参数外,都被无声地强制转换为数组 < p >例子:< br > // TODO: examples, anyone? < / p >

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)
)
);

我基于eis的回答创建了这个函数:

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))

这可能是最简单的推理:

$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')
)

YaLinqo library*非常适合这类任务。它是。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调用。

*由我开发

下面是我的非常简单的、兼容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"
}

部分应用程序

如果你需要多次使用不同数组但相同映射函数的函数,你可以做一些叫做部分函数应用(与' 局部套用 '相关)的事情,它允许你在调用时只传入数据数组:

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

我一直喜欢数组映射的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;
});
);

这就是我如何在我的项目中实现它。

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));
}

我会这样做:

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

看过来!有一个简单的解决方案!

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));

我将使用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数组,其余的是不言自明的

用(out)保存键的另一种方法:

$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)
);

你可以从这个数组的图书馆中使用地图方法来轻松实现你想要的结果:

Arr::map($test_array, function($a, $b) { return "$a loves $b"; });

此外,它保留键并返回新的数组,更不用说几个不同的模式来满足您的需求。

$array = [
'category1' => 'first category',
'category2' => 'second category',
];
 

$new = array_map(function($key, $value) {
return "{$key} => {$value}";
}, array_keys($array), $array);

如果你只需要它一次,关闭就可以工作。我会使用发电机

$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)
));