开发者

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 f开发者_如何学Pythonor loop instead of a map?


Sure you can, with the help of 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 doesn't get access to the keys either. array_walk can access keys, but the array is passed by reference, which requires a layer of indirection.

Some solutions are:

Array of pairs

This is bad, since we're changing the original array. Plus the boilerplate "array()" calls increase linearly with the length of the array:

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

Temporary variable

We're acting on the original array, and the boilerplate is constant, but we can easily clobber an existing variable:

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

One-shot function

We can use function scope to prevent clobbering existing names, but have to add an extra layer of "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

The interesting thing to note is that our last one-shot function has a nice, generic signature and looks a lot like array_map. We might want to give this a name and re-use it:

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

Indirect Array Walk

When writing the above I'd ignored array_walk since it requires its argument to be passed by reference; however, I've since realised that it's easy to work around this using call_user_func. I think this is the best version so far:

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


There is no way to access the index within the array_map callback. If you are working with sequential numeric indices, then an incrementing static variable could be used:

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

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

print_r($mapped);

Resulting in:

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

When using this approach, it's important to use an anonymous function as the callback and to never reuse that anonymous function to avoid referencing the same static variable outside of array_map.


It's a bit of an old thread but as many of you, I'm using array_keys:

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

Edit: Instead of use keyword, you can add two arrays in second parameter of your arrray_map function. I think no explications needed, the code is pretty simple.


Very simple:

Only array_map fuction: does not have index key!

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

Now, combine with 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)


As mentioned by earlier posted answers, when keys and values are needed within the callback function's body, it is simplest to pass in the array of keys, then access the values for those keys via the original array.

With older versions that do not have arrow functions, use use() or global (I recommend the former) to allow access to the original array from within the callback.

With modern PHP (7.4+), use arrow function syntax. (Demo)

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

There is also no shame in using a foreach() and modifying the values by reference. (Demo)

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


You can create your own map function using 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);

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


For a fast and open solution (without doubling array using array_keys and similar):

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

Usage examples:

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


You can generate your own index to be used with 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);

As shown above, you may securely create an array of numbers from 0 to the length of your array, same as a index. Put this second array in the array_map and use it values as index in your function.


You can use the array_keys() function to get the keys of an array, and then loop through those keys using a foreach loop.

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜