web-dev-qa-db-fra.com

PHP: Puis-je obtenir l'index dans une fonction array_map?

J'utilise une carte en php comme ceci:

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

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

Est-il possible d'obtenir l'index de la valeur dans la fonction?

De plus, si j'écris du code qui a besoin de l'index, devrais-je utiliser une boucle for au lieu d'une carte?

59
Ollie Glass

Bien sûr, vous pouvez, avec l’aide 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);
158
Aron Rotteveel

Lors du mappage d'une fonction anonyme sur un tableau anonyme, il n'y a aucun moyen d'accéder aux clés:

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

array_reduce n'a pas non plus accès aux clés. array_walk peut accéder aux clés, mais le tableau est passé par référence, ce qui nécessite une couche d'indirection.

Certaines solutions sont:

Tableau de paires

C'est mauvais, car nous changeons le tableau d'origine. De plus, les appels "array ()" "boilerplate" augmentent linéairement avec la longueur du tableau:

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

Variable temporaire

Nous agissons sur le tableau d'origine et le passe-partout est constant, mais nous pouvons facilement décomposer une variable existante:

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

Fonction One-Shot

Nous pouvons utiliser l'étendue de la fonction pour éviter de surcharger les noms existants, mais nous devons ajouter une couche supplémentaire "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,
          /* ... */));

Fonction one-shot multi-arguments

Nous définissons la fonction que nous mappons dans le champ d’application original pour empêcher l’utilisation "habituelle":

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

Nouvelle fonction

La chose intéressante à noter est que notre dernière fonction one-shot a une belle signature générique et ressemble beaucoup à array_map. Nous voudrons peut-être donner à cela un nom et le réutiliser:

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

Notre code d'application devient alors:

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

Marche indirecte

Lors de l'écriture de ce qui précède, j'avais ignoré array_walk car il fallait que son argument soit transmis par référence; Cependant, j'ai depuis réalisé qu'il était facile de contourner ce problème en utilisant call_user_func. Je pense que c'est la meilleure version à ce jour:

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

Il vous manque un paramètre dans la classe Dropdown. Je pense que c'est mieux comme ça:

public function options()
{
    $value = $this->value;

    $mapped = array_map(function($k, $v) use ($value) {
     return array(
        'value'    => $k,
        'display'  => $v,
        'selected' => ($value === $k),
      );
    },  array_keys($this->options), $this->options);

    return $mapped;
}
0
MatthieuH