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?

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

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

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)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.