PHP:我能在array_map函数中获取索引吗?

124

我正在使用 PHP 中的 map,代码如下:

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

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

在函数中是否可以获取值的索引?

另外,如果我编写需要索引的代码,是应该使用for循环还是map?

11个回答

274

当然可以,借助于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);

1
@Gordon 是的,你可以向 array_map() 提供任意数量的参数 :) - Aron Rotteveel
21
这种方法风险很高,因为 PHP 不能保证 array_keys 返回的键与原始数组中的顺序相同。因此,您可能会将键映射到错误的值。安全的做法是仅将 array_keys 作为 array_map 的第二个参数,并使用 use 语句将数组传递给闭包。 - user487772
24
我实在不理解为什么PHP没有提供一个map函数,可以将每个元素的键作为回调函数的第二个参数。 - flu
1
@TimBezhashvyly,您能否提供PHP文档的链接来详细说明一下?http://php.net/manual/en/function.array-keys.php并没有说明顺序不能保证的问题。 - flu
3
我同意在这种情况下最好不要使用array_keys。相反,我们可以使用range(0, count($array)-1)来获取索引值。 - Dave F
显示剩余4条评论

16

在对匿名数组执行匿名函数映射时,无法访问键:

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,
          /* ... */));

多参数一次性函数

我们在原始作用域中定义要映射的函数,以避免“use”样板代码:

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

新函数

有趣的是,我们上一个一次性函数具有良好的通用签名,看起来很像array_map。我们可能想要给它一个名称并重复使用:

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

我们的应用程序代码变成:

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) { /* ... */ });

13

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之外引用相同的静态变量。

5

这是一个有点老的帖子,但和许多人一样,我正在使用array_keys函数:

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

编辑:在您的array_map函数的第二个参数中,您可以添加两个数组代替use关键字。我认为不需要进行解释,这段代码非常简单。

1
array_values($array) 可以安全地替换为 $array。如果您不打算使用 array_map()return,那么您可能不应该调用 array_map()。很可能 array_walk() 更适合。 - mickmackusa

2
如早先发布的答案所提到的,当需要在回调函数体内使用键和值时,最简单的方法是传入键数组,然后通过原始数组访问这些键对应的值。
对于没有箭头函数的旧版本,请使用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);

2

非常简单:

只需使用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_keys:
$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)

0
你可以生成自己的索引与 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到您的数组的长度,就像索引一样。 将这个第二个数组放入array_map中,并将其值用作函数中的索引。

1
我没有看到 func 常量的定义。你没有测试这段代码。请在发布到 Stack Overflow 之前在 3v4l.org 上测试你的代码片段。 - mickmackusa
1
调用两个函数(range(0, count($values) - 1))来生成已经生成的数据并不是一个好主意。只需使用array_keys()访问已经可用的内容即可。 - mickmackusa

0

对于一个快速且开放的解决方案(不使用array_keys等方法来重复数组):

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

0
您可以使用foreach创建自己的映射函数:
<?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


0
一个简单的代码行,我用它来构建PDO查询。效果很好。在这种情况下使用了匿名函数。
$values = array(4, 6, 3);
$mapped = array_map(function($v,$i) {return $v*2;}, $values, range(0,count($values)-1));
var_dump($mapped);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接