PHP: ¿Puedo obtener el índice en una función array_map?

84

Estoy usando un mapa en php así:

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

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

¿Es posible obtener el índice del valor en la función?

Además, si estoy escribiendo código que necesita el índice, ¿debería usar un bucle for en lugar de un mapa?

Ollie Glass
fuente

Respuestas:

209

Seguro que puedes, con la ayuda de 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);
Aron Rotteveel
fuente
20
Buena respuesta, no me di cuenta de que podía pasar parámetros adicionales a un método array_map () ped. ¡Aprenda algo nuevo cada día!
GordonM
1
@Gordon, sí, puede proporcionar array_map()un número arbitrario de argumentos :)
Aron Rotteveel
13
Este es un enfoque muy arriesgado ya que PHP no garantiza que las claves devueltas por array_keyspermanecerán en el mismo orden que en la matriz original. Por lo tanto, podría terminar asignando claves a valores incorrectos. El enfoque seguro es usar solo array_keyscomo segundo argumento de array_mapy luego pasar la matriz al cierre con la usedeclaración.
user487772
12
Francamente, no entiendo por qué PHP no tiene una función de mapa que proporcione la clave de cada elemento como segundo parámetro de la devolución de llamada.
gripe
1
@flu PHP no se ganó el título de lenguaje inapropiado sin ninguna razón.
xZero
9

Al mapear una función anónima sobre una matriz anónima, no hay forma de acceder a las claves:

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

array_reduce tampoco tiene acceso a las claves. array_walk puede acceder a las claves, pero la matriz se pasa por referencia, lo que requiere una capa de indirección.

Algunas soluciones son:

Matriz de pares

Esto es malo, ya que estamos cambiando la matriz original. Además, las llamadas repetitivas "array ()" aumentan linealmente con la longitud de la matriz:

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

Variable temporal

Estamos actuando sobre la matriz original, y el texto estándar es constante, pero podemos golpear fácilmente una variable existente:

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

Función de disparo único

Podemos usar el alcance de la función para evitar aplastar los nombres existentes, pero tenemos que agregar una capa adicional de "uso":

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

Función one-shot de múltiples argumentos

Definimos la función que estamos mapeando en el alcance original para evitar el "uso" repetitivo):

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

Nueva función

Lo interesante a tener en cuenta es que nuestra última función one-shot tiene una bonita firma genérica y se parece mucho a array_map. Es posible que queramos darle un nombre y reutilizarlo:

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

Nuestro código de aplicación se convierte en:

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

Paseo indirecto por matriz

Al escribir lo anterior, ignoré array_walk ya que requiere que su argumento se pase por referencia; sin embargo, desde entonces me di cuenta de que es fácil solucionar esto usando call_user_func. Creo que esta es la mejor versión hasta ahora:

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

Muy simple:

Solo función array_map: ¡no tiene clave de índice!

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

Ahora, combine con 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)
Fábio Zangirolami
fuente