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

6 minutos de lectura

avatar de usuario
vidrio de ollie

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 un código que necesita el índice, ¿debería usar un bucle for en lugar de un mapa?

avatar de usuario
Aron Rotteveel

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

  • @Gordon, sí, puedes suministrar array_map() con un número arbitrario de argumentos 🙂

    – Aron Rotteveel

    3 mayo 2011 a las 11:17

  • Este es un enfoque muy arriesgado ya que PHP no garantiza que las claves devueltas por array_keys permanecerá en el mismo orden que en la matriz original. Por lo tanto, podría terminar asignando claves a valores incorrectos. El enfoque seguro es utilizar sólo array_keys como segundo argumento de array_map y luego pasar la matriz al cierre con use declaración.

    – usuario487772

    02/12/2015 a las 21:01

  • 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

    22 de julio de 2016 a las 9:45

  • @TimBezhashvyly ¿Puede explicarlo con un enlace a la documentación de PHP? php.net/manual/en/function.array-keys.php No dice nada acerca de que el pedido no esté garantizado.

    – gripe

    22 de julio de 2016 a las 9:47


  • Estoy de acuerdo en que es mejor no usar array_keys en esta situación. En cambio, podemos obtener los índices usando: range(0, count($array)-1)

    – Dave F.

    10 de abril de 2018 a las 4:09


avatar de usuario
Warbo

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 direccionamiento indirecto.

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 modelo es constante, pero podemos eliminar 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 un solo disparo

Podemos usar el alcance de la función para evitar la destrucción de nombres existentes, pero debemos 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 multiargumento one-shot

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 única tiene una firma genérica agradable y se parece mucho a array_map. Podríamos querer darle un nombre a esto y reutilizarlo:

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

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

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

Paseo de matriz indirecta

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

avatar de usuario
Evan Byrne

No hay forma de acceder al índice dentro del array_map llamar de vuelta. Si está trabajando con índices numéricos secuenciales, se podría usar una variable estática incremental:

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

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

print_r($mapped);

Resultando en:

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

Al utilizar este enfoque, es importante utilizar un función anónima como devolución de llamada y nunca reutilizar esa función anónima para evitar hacer referencia a la misma variable estática fuera de array_map.

avatar de usuario
yannick francois

Es un hilo un poco viejo, pero como muchos de ustedes, estoy usando array_keys :

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

editar: en lugar de usar una palabra clave, puede agregar dos matrices en el segundo parámetro de su función arrray_map. Creo que no se necesitan explicaciones, el código es bastante simple.

Muy simple:

Solo la 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)

avatar de usuario
domis86

Puede crear su propia función de mapa usando 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);

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

avatar de usuario
mario gri

Para una solución rápida y abierta (sin duplicar la matriz usando array_keys y similares):

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

Ejemplos de uso:

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad