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

PhpArraysLoopsDictionaryIteration

Php Problem Overview


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?

Php Solutions


Solution 1 - Php

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

Solution 2 - Php

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

Solution 3 - Php

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.

Solution 4 - Php

It's a bit 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.

Solution 5 - Php

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)

Solution 6 - Php

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

Solution 7 - Php

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 )

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionOllie GlassView Question on Stackoverflow
Solution 1 - PhpAron RotteveelView Answer on Stackoverflow
Solution 2 - PhpWarboView Answer on Stackoverflow
Solution 3 - PhpEvan ByrneView Answer on Stackoverflow
Solution 4 - PhpYannick FrançoisView Answer on Stackoverflow
Solution 5 - PhpFábio ZangirolamiView Answer on Stackoverflow
Solution 6 - Phpdomis86View Answer on Stackoverflow
Solution 7 - PhpMarius GriView Answer on Stackoverflow