【问题标题】:PHP: Can I get the index in an array_map function?PHP:我可以在 array_map 函数中获取索引吗?
【发布时间】:2011-08-17 15:05:39
【问题描述】:

我在 php 中使用这样的地图:

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

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

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

另外 - 如果我正在编写需要索引的代码,我应该使用 for 循环而不是映射吗?

【问题讨论】:

    标签: php arrays loops dictionary iteration


    【解决方案1】:

    当然可以,在 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);
    

    【讨论】:

    • 很酷的答案,没有意识到您可以将额外的参数传递给 array_map()ped 方法。每天学习新东西!
    • 这是一种非常危险的方法,因为 PHP 不保证 array_keys 返回的键将保持与原始数组中相同的顺序。因此,您最终可能会将键映射到错误的值。安全的方法是只使用array_keys 作为array_map 的第二个参数,然后使用use 语句将数组传递给闭包。
    • 老实说我不明白为什么 PHP 没有一个 map 函数来提供每个元素的键作为回调的第二个参数。
    • @flu PHP 没有无缘无故没有获得糟糕语言的称号。
    • 我同意在这种情况下最好不要使用array_keys。相反,我们可以使用 range(0, count($array)-1) 获取索引
    【解决方案2】:

    将匿名函数映射到匿名数组时,无法访问键:

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

    一次性功能

    我们可以使用函数范围来防止破坏现有名称,但必须添加一个额外的“使用”层:

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

    多参数一次性函数

    我们在原始范围内定义我们要映射的函数,以防止“使用”样板文件):

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

    【讨论】:

      【解决方案3】:

      很简单:

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

      【讨论】:

        【解决方案4】:

        您可以使用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

        【讨论】:

          【解决方案5】:

          对于一个快速和开放的解决方案(不使用 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 )
          

          【讨论】:

            【解决方案6】:

            无法在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
            )
            

            使用这种方法时,重要的是使用 anonymous function 作为回调,并且永远不要重复使用该匿名函数以避免在 array_map 之外引用相同的静态变量。

            【讨论】:

              【解决方案7】:

              这有点老了,但和你们一样,我正在使用 array_keys :

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

              编辑:你可以在你的 array_map 函数的第二个参数中添加两个数组,而不是使用关键字。我觉得不需要解释,代码很简单。

              【讨论】:

                猜你喜欢
                • 2017-03-18
                • 1970-01-01
                • 2010-11-07
                • 2010-11-15
                • 2020-08-31
                • 2014-09-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多