Y a-t-il un moyen de faire quelque chose comme ça:
$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
et array_values
, en passant directement la variable $test_array
?
La sortie souhaitée est:
array(2) {
[0]=>
string(27) "first_key loves first_value"
[1]=>
string(29) "second_key loves second_value"
}
Pas avec array_map, car il ne gère pas les clés.
array_walk fait:
$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"
// }
Cependant, cela modifie le tableau donné en paramètre, donc ce n'est pas une programmation fonctionnelle (vous avez la question étiquetée comme ça). En outre, comme indiqué dans le commentaire, cela ne modifiera que les valeurs du tableau, les clés ne seront donc pas celles que vous avez spécifiées dans la question.
Vous pouvez écrire une fonction qui corrige les points au-dessus de vous si vous le souhaitez, 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"
// }
C'est probablement le plus court et le plus facile à raisonner sur:
$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')
)
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'appelable que vous fournissez doit lui-même renvoyer un tableau avec deux valeurs, à savoir return [key, value]
. L'appel interne à array_map
génère donc un tableau de tableaux. Ceci est ensuite reconverti en un tableau à une dimension par array_column
.
$ordinals = [
'first' => '1st',
'second' => '2nd',
'third' => '3rd',
];
$func = function ($k, $v) {
return ['new ' . $k, 'new ' . $v];
};
var_dump(array_map_assoc($func, $ordinals));
array(3) {
["new first"]=>
string(7) "new 1st"
["new second"]=>
string(7) "new 2nd"
["new third"]=>
string(7) "new 3rd"
}
Si vous devez utiliser la fonction plusieurs fois avec plusieurs tableaux, mais avec la même fonction de mappage, vous pouvez effectuer une opération appelée application de fonction partielle (liée à ' currying '), qui vous permet uniquement de passer 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
et $ordinals
sont comme précédemment.
REMARQUE: si votre fonction mappée retourne la même clé pour deux entrées différentes, la valeur associée à la clé la plus récente l'emportera. Inversez le tableau d'entrée et le résultat de sortie de array_map_assoc
pour permettre aux clés précédentes de gagner. (Les clés renvoyées dans mon exemple ne peuvent pas entrer en collision car elles incorporent la clé du tableau source, qui doit être unique.)
Voici une variante de ce qui précède, qui pourrait s'avérer plus logique pour certains, mais 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 plutôt renvoyer un tableau associatif avec une ligne, c'est-à-dire return [key => value]
. Le résultat du mappage de l'appelable est alors simplement décompressé et passé à array_merge
. Comme précédemment, le renvoi d'une clé en double entraîne la victoire des valeurs ultérieures.
nb Alex83690 a noté dans un commentaire que l'utilisation de
array_replace
ici au lieu dearray_merge
permettrait de conserver les clés entières.array_replace
ne modifie pas le tableau d'entrée, il est donc sans danger pour le code fonctionnel.
Si vous êtes sur PHP 5.3 à 5.5, ce qui suit est équivalent. Il utilise array_reduce
et l'opérateur de tableau binaire +
pour convertir le tableau à deux dimensions obtenu en un tableau à une dimension 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;
}, []);
}
Ces deux variantes seraient utilisées ainsi:
$ordinals = [
'first' => '1st',
'second' => '2nd',
'third' => '3rd',
];
$func = function ($k, $v) {
return ['new ' . $k => 'new ' . $v];
};
var_dump(array_map_assoc($func, $ordinals));
Notez le =>
au lieu de ,
dans $func
.
La sortie est la même que précédemment et chacune peut être partiellement appliquée de la même manière qu'avant.
Le but de la question initiale est de simplifier le plus possible l'appel de l'appel, au détriment d'une fonction plus complexe invoquée. en particulier, pour pouvoir passer le tableau de données en tant qu’argument unique, sans fractionner 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 la fonction array_map_assoc()
qui supprime les clés de sortie, car 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));
Donc, la réponse est NO, vous ne pouvez pas éviter d'appeler array_keys
, mais vous pouvez résumer l'endroit où array_keys
est appelé dans une fonction d'ordre supérieur, ce qui peut être suffisant.
Avec PHP 5.3 ou plus tard:
$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)
)
);
Voici comment j'ai implémenté cela dans mon projet.
function array_map_associative(callable $callback, $array) {
/* map original array keys, and call $callable with $key and value of $key from original array. */
return array_map(function($key) use ($callback, $array){
return $callback($key, $array[$key]);
}, array_keys($array));
}
YaLinqo library * convient parfaitement à ce type de tâche. C'est un port de LINQ à partir de .NET qui supporte pleinement les valeurs et les clés dans tous les rappels et ressemble à SQL. Par exemple:
$mapped_array = from($test_array)
->select(function ($v, $k) { return "$k loves $v"; })
->toArray();
ou juste:
$mapped_iterator = from($test_array)->select('"$k loves $v"');
'"$k loves $v"'
est ici un raccourci pour la syntaxe de fermeture complète prise en charge par cette bibliothèque. toArray()
à la fin est facultatif. La chaîne de méthodes renvoie un itérateur. Par conséquent, si le résultat doit simplement être itéré à l'aide de foreach
, l'appel toArray
peut être supprimé.
* développé par moi
D'après la réponse de eis , voici ce que j'ai finalement fait pour éviter de gâcher le tableau d'origine:
$test_array = array("first_key" => "first_value",
"second_key" => "second_value");
$result_array = array();
array_walk($test_array,
function($a, $b) use (&$result_array)
{ $result_array[] = "$b loves $a"; },
$result_array);
var_dump($result_array);
Par "boucle manuelle", je voulais dire écrire une fonction personnalisée qui utilise foreach
. Cela retourne un nouveau tableau comme array_map
parce que la portée de la fonction fait que $array
est une copie et non une référence:
function map($array, callable $fn) {
foreach ($array as $k => &$v) $v = call_user_func($fn, $k, $v);
return $array;
}
Votre technique utilisant array_map
avec array_keys
semble en réalité plus simple et plus puissante, car vous pouvez utiliser null
comme rappel pour renvoyer les paires clé-valeur:
function map($array, callable $fn = null) {
return array_map($fn, array_keys($array), $array);
}
J'ai créé cette fonction en me basant sur la réponse de eis :
function array_map_($callback, $arr) {
if (!is_callable($callback))
return $arr;
$result = array_walk($arr, function(&$value, $key) use ($callback) {
$value = call_user_func($callback, $key, $value);
});
if (!$result)
return false;
return $arr;
}
Exemple:
$test_array = array("first_key" => "first_value",
"second_key" => "second_value");
var_dump(array_map_(function($key, $value){
return $key . " loves " . $value;
}, $arr));
Sortie:
array (
'first_key' => 'first_key loves first_value,
'second_key' => 'second_key loves second_value',
)
Bien sûr, vous pouvez utiliser array_values
pour renvoyer exactement ce que veut OP.
array_values(array_map_(function($key, $value){
return $key . " loves " . $value;
}, $test_array))
Regarde ici! Il y a une solution triviale!
function array_map2(callable $f, array $a)
{
return array_map($f, array_keys($a), $a);
}
Comme indiqué dans la question, array_map
a déjà exactement la fonctionnalité requise. Les autres réponses ici trop compliquent sérieusement les choses: array_walk
n'est pas fonctionnel.
Utilisation
Exactement ce que vous attendez de votre exemple:
$test_array = array("first_key" => "first_value",
"second_key" => "second_value");
var_dump(array_map2(function($a, $b) { return "$a loves $b"; }, $test_array));
Je ferais quelque chose comme ça:
<?php
/**
* array_map_kv()
* An array mapping function to map with both keys and values.
*
* @param $callback callable
* A callback function($key, $value) for mapping values.
* @param $array array
* An array for mapping.
*/
function array_map_kv(callable $callback, array $array) {
return array_map(
function ($key) use ($callback, $array) {
return $callback($key, $array[$key]); // $callback($key, $value)
},
array_keys($array)
);
}
// use it
var_dump(array_map_kv(function ($key, $value) {
return "{$key} loves {$value}";
}, array(
"first_key" => "first_value",
"second_key" => "second_value",
)));
?>
Résultats:
array(2) {
[0]=>
string(27) "first_key loves first_value"
[1]=>
string(29) "second_key loves second_value"
}
J'ajouterai une autre solution au problème avec la version 5.6 ou ultérieure. Je ne sais pas si c'est plus efficace que les solutions déjà géniales (probablement pas), mais pour moi, c'est simplement plus simple à lire:
$myArray = [
"key0" => 0,
"key1" => 1,
"key2" => 2
];
array_combine(
array_keys($myArray),
array_map(
function ($intVal) {
return strval($intVal);
},
$myArray
)
);
En utilisant strval()
comme exemple de fonction dans le array_map
, ceci générera:
array(3) {
["key0"]=>
string(1) "0"
["key1"]=>
string(1) "1"
["key2"]=>
string(1) "2"
}
Espérons que je ne suis pas le seul à trouver cela assez simple à comprendre .array_combine
crée un tableau key => value
à partir d'un tableau de clés et d'un tableau de valeurs, le reste est assez explicite.
J'aime toujours la variante javascript de la carte matricielle. La version la plus simple serait:
/**
* @param array $array
* @param callable $callback
* @return array
*/
function arrayMap(array $array, callable $callback)
{
$newArray = [];
foreach( $array as $key => $value )
{
$newArray[] = call_user_func($callback, $value, $key, $array);
}
return $newArray;
}
Alors maintenant, vous pouvez simplement lui passer une fonction de rappel pour construire les valeurs.
$testArray = [
"first_key" => "first_value",
"second_key" => "second_value"
];
var_dump(
arrayMap($testArray, function($value, $key) {
return $key . ' loves ' . $value;
});
);
Je vois qu'il manque la réponse évidente:
function array_map_assoc(){
if(func_num_args() < 2) throw new \BadFuncionCallException('Missing parameters');
$args = func_get_args();
$callback = $args[0];
if(!is_callable($callback)) throw new \InvalidArgumentException('First parameter musst be callable');
$arrays = array_slice($args, 1);
array_walk($arrays, function(&$a){
$a = (array)$a;
reset($a);
});
$results = array();
$max_length = max(array_map('count', $arrays));
$arrays = array_map(function($pole) use ($max_length){
return array_pad($pole, $max_length, null);
}, $arrays);
for($i=0; $i < $max_length; $i++){
$elements = array();
foreach($arrays as &$v){
$elements[] = each($v);
}
unset($v);
$out = call_user_func_array($callback, $elements);
if($out === null) continue;
$val = isset($out[1]) ? $out[1] : null;
if(isset($out[0])){
$results[$out[0]] = $val;
}else{
$results[] = $val;
}
}
return $results;
}
Fonctionne exactement comme array_map. Presque.
En fait, ce n'est pas pur map
comme vous le savez dans d'autres langues. Php est très étrange, il nécessite donc des fonctions utilisateur très étranges, car nous ne voulons pas casser notre approche worse is better
avec précision.
Vraiment, ce n'est pas réellement map
du tout. Pourtant, cela reste très utile.
La première différence évidente avec array_map est que le rappel prend les sorties de each()
de chaque tableau en entrée au lieu de la valeur seule. Vous pouvez toujours parcourir plusieurs tableaux à la fois.
La deuxième différence est la manière dont la clé est traitée après son retour de rappel; la valeur de retour de la fonction de rappel doit être array('new_key', 'new_value')
. Les clés peuvent et seront modifiées, les mêmes clés peuvent même provoquer le remplacement de la valeur précédente, si la même clé était renvoyée. Ce n'est pas un comportement commun de map
, mais il vous permet de réécrire des clés.
Troisièmement, si vous omettez key
dans la valeur renvoyée (par array(1 => 'value')
ou array(null, 'value')
), une nouvelle clé va être affectée, comme si $array[] = $value
était utilisé. Ce n'est pas non plus le comportement habituel de map
, mais il est parfois utile, je suppose.
La quatrième chose étrange est que, si la fonction de rappel ne renvoie pas de valeur ou renvoie null
, l'ensemble complet des clés et des valeurs actuelles est omis de la sortie, il est simplement ignoré. Cette fonctionnalité est totalement unmap
py, mais elle ferait de cette fonction un excellent cascadeur double pour array_filter_assoc
, si une telle fonction existait.
Si vous omettez le deuxième élément (1 => ...
) (le value part) dans le retour du rappel, null
est utilisé à la place de la valeur réelle.
Tous les autres éléments sauf ceux avec les clés 0
et 1
dans le retour du rappel sont ignorés.
Et enfin, si lambda renvoie une valeur autre que null
ou un tableau, elle est traitée comme si la clé et la valeur avaient été omises, ainsi:
null
est utilisé comme valeur ATTENTION:
N'oubliez pas que cette dernière fonctionnalité n'est qu'un résidu des fonctionnalités précédentes et qu'elle est probablement totalement inutile. Il est fortement déconseillé de s’appuyer sur cette fonctionnalité, car elle sera aléatoire. obsolète et changé de manière inattendue dans les versions futures .
REMARQUE:
Contrairement à array_map
, tous les paramètres autres que les tableaux transmis à array_map_assoc
, à l'exception du premier paramètre de rappel, sont transférés en mode silencieux dans des tableaux.
EXEMPLES:// TODO: examples, anyone?
Une autre façon de faire cela avec (out) préserver les clés:
$test_array = [
"first_key" => "first_value",
"second_key" => "second_value"
];
$f = function($ar) {
return array_map(
function($key, $val) {
return "{$key} - {$val}";
},
array_keys($ar),
$ar
);
};
#-- WITHOUT preserving keys
$res = $f($test_array);
#-- WITH preserving keys
$res = array_combine(
array_keys($test_array),
$f($test_array)
);
Vous pouvez utiliser la méthode map de cette bibliothèque de tableaux pour obtenir exactement ce que vous voulez, aussi facilement que:
Arr::map($test_array, function($a, $b) { return "$a loves $b"; });
de plus, il préserve les clés et renvoie un nouveau tableau, sans oublier quelques modes différents pour répondre à vos besoins.