311 votes

Accéder aux clés de premier niveau avec array_map() sans appeler `array_keys()`

Existe-t-il un moyen de faire quelque chose de ce genre ?

$test_array = array(
    "first_key" => "first_value", 
    "second_key" => "second_value"
);

var_dump(
    array_map(
        function($a, $b) {
            return "$a loves $b";
        }, 
        array_keys($test_array), 
        array_values($test_array)
    )
);

Mais au lieu d'appeler array_keys y array_values en transmettant directement le $test_array variable ?

Le résultat souhaité est le suivant :

array(2) {
  [0]=>
  string(27) "first_key loves first_value"
  [1]=>
  string(29) "second_key loves second_value"
}

291voto

eis Points 14687

Pas avec array_map, qui ne gère pas les clés.

array_walk ne :

$test_array = array("first_key" => "first_value",
                    "second_key" => "second_value");
array_walk($test_array, function(&$a, $b) { $a = "$b loves $a"; });
var_dump($test_array);

// array(2) {
//   ["first_key"]=>
//   string(27) "first_key loves first_value"
//   ["second_key"]=>
//   string(29) "second_key loves second_value"
// }

Il modifie cependant le tableau donné en paramètre, ce qui n'est pas exactement de la programmation fonctionnelle (puisque la question est ainsi libellée). De plus, comme indiqué dans le commentaire, cela ne changera que les valeurs du tableau, donc les clés ne seront pas celles que vous avez spécifiées dans la question.

Vous pourriez écrire une fonction qui corrige les points ci-dessus vous-même si vous le souhaitiez, comme ceci :

function mymapper($arrayparam, $valuecallback) {
  $resultarr = array();
  foreach ($arrayparam as $key => $value) {
    $resultarr[] = $valuecallback($key, $value);
  }
  return $resultarr;
}

$test_array = array("first_key" => "first_value",
                    "second_key" => "second_value");
$new_array = mymapper($test_array, function($a, $b) { return "$a loves $b"; });
var_dump($new_array);

// array(2) {
//   [0]=>
//   string(27) "first_key loves first_value"
//   [1]=>
//   string(29) "second_key loves second_value"
// }

223voto

Kevin Beal Points 1078

C'est probablement le plus court et le plus facile à raisonner :

$states = array('az' => 'Arizona', 'al' => 'Alabama');

array_map(function ($short, $long) {
    return array(
        'short' => $short,
        'long'  => $long
    );
}, array_keys($states), $states);

// produces:
array(
     array('short' => 'az', 'long' => 'Arizona'), 
     array('short' => 'al', 'long' => 'Alabama')
)

96voto

Nicholas Points 2862

Voici ma solution très simple, compatible avec PHP 5.5 :

function array_map_assoc(callable $f, array $a) {
    return array_column(array_map($f, array_keys($a), $a), 1, 0);
}

L'appel que vous fournissez doit lui-même renvoyer un tableau avec deux valeurs, c'est-à-dire return [key, value] . L'appel interne à array_map produit donc un tableau de tableaux. Celui-ci est ensuite reconverti en un tableau à une dimension par array_column .

Utilisation

$ordinals = [
    'first' => '1st',
    'second' => '2nd',
    'third' => '3rd',
];

$func = function ($k, $v) {
    return ['new ' . $k, 'new ' . $v];
};

var_dump(array_map_assoc($func, $ordinals));

Sortie

array(3) {
  ["new first"]=>
  string(7) "new 1st"
  ["new second"]=>
  string(7) "new 2nd"
  ["new third"]=>
  string(7) "new 3rd"
}

Application partielle

Si vous devez utiliser la fonction plusieurs fois avec des tableaux différents mais la même fonction de mappage, vous pouvez faire quelque chose qui s'appelle application d'une fonction partielle (lié à ' curry '), ce qui permet de ne transmettre que le tableau de données lors de l'invocation :

function array_map_assoc_partial(callable $f) {
    return function (array $a) use ($f) {
        return array_column(array_map($f, array_keys($a), $a), 1, 0);
    };
}

...
$my_mapping = array_map_assoc_partial($func);
var_dump($my_mapping($ordinals));

Ce qui produit le même résultat, étant donné que $func y $ordinals sont les mêmes que précédemment.

REMARQUE : si votre fonction cartographiée renvoie la valeur même clé pour deux entrées différentes, la valeur associée à la clé la plus tardive l'emportera. Inverser le tableau d'entrée et le résultat de sortie de array_map_assoc pour permettre aux clés antérieures de l'emporter. (Dans mon exemple, les clés renvoyées ne peuvent pas entrer en collision, car elles intègrent la clé du tableau source, qui doit à son tour être unique).


Alternative

Voici une variante de ce qui précède, qui pourrait s'avérer plus logique pour certains, mais qui nécessite PHP 5.6 :

function array_map_assoc(callable $f, array $a) {
    return array_merge(...array_map($f, array_keys($a), $a));
}

Dans cette variante, la fonction fournie (sur laquelle le tableau de données est mappé) doit renvoyer un tableau associatif d'une ligne, c'est-à-dire return [key => value] . Le résultat du mappage de l'objet appelable est alors simplement décompressé et transmis à array_merge . Comme précédemment, le fait de renvoyer une clé dupliquée aura pour effet de faire gagner les valeurs ultérieures.

n.b. Alex83690 a noté dans un commentaire que l'utilisation de array_replace ici à la place de array_merge préserverait les clés entières. array_replace ne modifie pas le tableau d'entrée, et est donc sans danger pour le code fonctionnel.

Si vous êtes en PHP 5.3 à 5.5, ce qui suit est équivalent. Il utilise array_reduce et le code binaire + pour convertir le tableau bidimensionnel résultant en un tableau unidimensionnel tout en préservant les clés :

function array_map_assoc(callable $f, array $a) {
    return array_reduce(array_map($f, array_keys($a), $a), function (array $acc, array $a) {
        return $acc + $a;
    }, []);
}

Utilisation

Ces deux variantes seraient ainsi utilisées :

$ordinals = [
    'first' => '1st',
    'second' => '2nd',
    'third' => '3rd',
];

$func = function ($k, $v) {
    return ['new ' . $k => 'new ' . $v];
};

var_dump(array_map_assoc($func, $ordinals));

Notez que les => au lieu de , en $func .

Le résultat est le même que précédemment, et chacun peut être partiellement appliqué de la même manière que précédemment.


Résumé

L'objectif de la question initiale est de rendre l'invocation de l'appel aussi simple que possible, au détriment de l'invocation d'une fonction plus compliquée ; en particulier, il s'agit de pouvoir passer le tableau de données en tant qu'argument unique, sans diviser les clés et les valeurs. En utilisant la fonction fournie au début de cette réponse :

$test_array = ["first_key" => "first_value",
               "second_key" => "second_value"];

$array_map_assoc = function (callable $f, array $a) {
    return array_column(array_map($f, array_keys($a), $a), 1, 0);
};

$f = function ($key, $value) {
    return [$key, $key . ' loves ' . $value];
};

var_dump(array_values($array_map_assoc($f, $test_array)));

Ou, pour cette question uniquement, nous pouvons simplifier en disant array_map_assoc() qui supprime les clés de sortie, puisque la question ne les demande pas :

$test_array = ["first_key" => "first_value",
               "second_key" => "second_value"];

$array_map_assoc = function (callable $f, array $a) {
    return array_map($f, array_keys($a), $a);
};

$f = function ($key, $value) {
    return $key . ' loves ' . $value;
};

var_dump($array_map_assoc($f, $test_array));

La réponse est donc NON vous ne pouvez pas éviter d'appeler array_keys mais vous pouvez faire abstraction de l'endroit où array_keys est appelé dans une fonction d'ordre supérieur, ce qui peut être suffisant.

64voto

Ostap34PHP Points 97
$array = [
  'category1' => 'first category',
  'category2' => 'second category',
];

$new = array_map(function($key, $value) {
  return "{$key} => {$value}";
}, array_keys($array), $array);

21voto

Tadas Sasnauskas Points 949

Avec PHP5.3 ou supérieur :

$test_array = array("first_key" => "first_value", 
                    "second_key" => "second_value");

var_dump(
    array_map(
        function($key) use ($test_array) { return "$key loves ${test_array[$key]}"; },
        array_keys($test_array)
    )
);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X