PHP: Can I get the index in an array_map function?

I'm using a map in php like so:

function func($v) {
return $v * 2;
}


$values = array(4, 6, 3);
$mapped = array_map(func, $values);
var_dump($mapped);

Is it possible to get the index of the value in the function?

Also - if I'm writing code that needs the index, should I be using a for loop instead of a map?

83913 次浏览

当然可以,在 array_keys():的帮助下

function func($v, $k)
{
// key is now $k
return $v * 2;
}


$values = array(4, 6, 3);
$mapped = array_map('func', $values, array_keys($values));
var_dump($mapped);

When mapping an anonymous function over an anonymous array, there is no way to access the keys:

array_map(
function($val) use ($foo) { /* ... */ },
array(key1 => val1,
key2 => val2,
/* ... */));

Array _ reduce 也不能访问键。Array _ walk 可以访问键,但是数组是通过引用传递的,这需要一个间接层。

一些解决方案是:

对数组

情况不妙,因为我们要改变原始数组。另外,样板“ array ()”调用会随着数组的长度线性增加:

array_map(
function($pair) use ($foo) {
list($key, $val) = $pair;
/* ... */
},
array(array(key1, val1),
array(key2, val2),
/* ... */));

临时变量

我们使用的是原始数组,样板文件是常量,但我们可以轻松地删除一个现有的变量:

$i_hope_this_does_not_conflict = array(key1 => val1,
key2 => val2,
/* ... */);
array_map(
function($key, $val) use ($foo) { /* ... */ },
array_keys($i_hope_this_does_not_conflict),
$i_hope_this_does_not_conflict);
unset($i_hope_this_does_not_conflict);

一次性功能

我们可以使用函数作用域来防止重复使用现有名称,但是必须添加一个额外的“ use”层:

call_user_func(
function($arr) use ($foo) {
return array_map(function($key, $val) use ($foo) { /* ... */ },
array_keys($arr),
$arr);
},
array(key1 => val1,
key2 => val2,
/* ... */));

Multi-argument one-shot function

We define the function we're mapping in the original scope to prevent the "use" boilerplate):

call_user_func(
function($f, $arr) {
return array_map($f, array_keys($arr), $arr);
},
function($key, $val) use ($foo) { /* ... */ },
array(key1 => val1,
key2 => val2,
/* ... */));

New function

值得注意的有趣的事情是,我们上一个一次性函数有一个很好的通用签名,看起来很像 array _ map。我们可以给它起个名字,然后重复使用:

function array_mapk($f, $arr) {
return array_map($f, array_keys($arr), $arr);
}

Our application code then becomes:

array_mapk(
function($key, $val) use ($foo) { /* ... */ },
array(key1 => val1,
key2 => val2,
/* ... */));

间接阵列步进

在编写上述代码时,我忽略了 array _ walk,因为它要求参数通过引用传递; 然而,我后来意识到使用 call _ user _ func 很容易解决这个问题。我认为这是目前为止最好的版本:

call_user_func(
'array_walk',
array(key1 => val1,
key2 => val2,
/* ... */),
function($val, $key) use ($foo) { /* ... */ });

很简单:

只有 array _ map 函数: 没有索引键!

 $params = [4,6,2,11,20];


$data = array_map(function($v) { return ":id{$v}";}, $params);


array (size=5)
0 => string ':id4' (length=4)
1 => string ':id6' (length=4)
2 => string ':id2' (length=4)
3 => string ':id11' (length=5)
4 => string ':id20' (length=5)

现在,结合 array _ key:

$data = array_map(
function($k) use ($params) { return ":id{$k}_${params[$k]}"; },
array_keys($params)
);


array (size=5)
0 => string ':id0_4' (length=6)
1 => string ':id1_6' (length=6)
2 => string ':id2_2' (length=6)
3 => string ':id3_11' (length=7)
4 => string ':id4_20' (length=7)

您可以使用 foreach创建自己的 map 函数:

<?php


function myCallback($key, $val)
{
var_dump("myCallback - key: $key, val: $val");
return $val * 2;
}


function foreachMap($callback, $givenArray) {
$result = [];
foreach ($givenArray as $key=>$val) {
$result[$key] = $callback($key, $val);
}
return $result;
}


$values = array(4, 6, 3);
$mapped = foreachMap('myCallback', $values);
var_dump($mapped);


试试: https://3v4l.org/pmFlB

对于快速开放的解决方案(不使用 array _ key 和类似的方法将数组加倍) :

/**
* Array map alternative to work with values and keys of single array.
*
* Callable receives $value and $index of $sourceArray as arguments
* If keys are not preserved via $preserveKeys - $keyCallback can be used to determinate key
*
* @param array $sourceArray
* @param callable|null $valueCallback
* @param callable|null $keyCallback
* @param bool $preserveKeys
* @return array
*/
function array_map_indexed(
array $sourceArray,
?callable $valueCallback = null,
?callable $keyCallback = null,
bool $preserveKeys = true
): array {
$newArray = [];


foreach ($sourceArray as $key => $value) {
if ($preserveKeys) {
$newArray[$keyCallback ? $keyCallback($value, $key) : $key] = $valueCallback
? $valueCallback($value, $key)
: $value;
} else {
$newArray[] = $valueCallback
? $valueCallback($value, $key)
: $value;
}
}


return $newArray;
}

用法例子:

$result = array_map_indexed(
[
'a' => 'aValue',
'b' => 'bValue',
],
function($value, $index) {
return [$value, $index];
},
);
//Array ( [a] => Array ( [0] => aValue [1] => a ) [b] => Array ( [0] => bValue [1] => b ) )


$result = array_map_indexed(
[
'a' => 'aValue',
'b' => 'bValue',
],
function($value, $index) {
return $index.$value;
},
null,
false
);
//Array ( [0] => aaValue [1] => bbValue )


$result = array_map_indexed(
[
'a' => 'aValue',
'b' => 'bValue',
],
null,
function($value, $index) {
return $value === 'aValue' ? 'specificKey' : $index;
},
);
//Array ( [specificKey] => aValue [b] => bValue )

无法访问 array_map回调中的索引。如果使用的是顺序数值索引,那么可以使用递增的静态变量:

$values = ["one", "two", "three"];


$mapped = array_map(function ($value) {
static $i = 0;
$result = "Index: $i, Value: $value";
$i++;
return $result;
}, $values);


print_r($mapped);

结果:

Array
(
[0] => Index: 0, Value: one
[1] => Index: 1, Value: two
[2] => Index: 2, Value: three
)

使用这种方法时,一定要使用 匿名函数作为回调函数,并且永远不要重用该匿名函数,以避免在 array _ map 之外引用相同的静态变量。

这是一个有点老的线索,但你们很多人,我使用 array_keys:

array_map(function($id, $name) {
print '<option value="'.$id.'">'.$name.'</option>';
}, array_keys($array), array_values($array));

编辑: 您可以在 arrray_map函数的第二个参数中添加两个数组,而不是 use关键字。我认为不需要解释,代码很简单。

您可以生成自己的索引,以便与 array _ map 一起使用:

function func($v, $index) {
return $v * 2;
}


$values = array(4, 6, 3);
$valuesIndex = range(0, count($values) - 1);
$mapped = array_map(func, $values, $valuesIndex);
var_dump($mapped);

如上所示,您可以安全地创建一个从0到数组长度的数组,与索引相同。 Put this second array in the array_map and use it values as index in your function.

正如前面公布的答案所提到的,当回调函数体中需要键和值时,最简单的方法是传递键数组,然后通过原始数组访问这些键的值。

对于没有箭头函数的旧版本,可以使用 use()global(我推荐使用前者)来允许从回调中访问原始数组。

使用现代 PHP (7.4 +) ,使用箭头函数语法

$values = [4, 6, 3];
var_export(
array_map(
fn($k) => "$k: " . $values[$k] * 2,
array_keys($values)
)
);

使用 foreach()并通过引用修改值也没有什么不好意思的

$values = [4, 6, 3];
foreach ($values as $k => &$v) {
$v = "$k: " . $v * 2;
}
var_export($values);

可以使用 array _ keys ()函数获取数组的键,然后使用 foreach 循环遍历这些键。

$keys = array_keys($array);
foreach($keys as $key){
// do something with $key
}