una - php array_walk use
PHP array_map incluyendo claves (11)
Aquí está mi solución muy simple, compatible con PHP 5.5:
function array_map_assoc(callable $f, array $a) {
return array_column(array_map($f, array_keys($a), $a), 1, 0);
}
El invocable que usted suministra debe devolver una matriz con dos valores, es decir, return [key, value]
. La llamada interna a array_map
por array_map
tanto produce una matriz de matrices. Luego, esto se convierte de nuevo en una matriz de una sola dimensión mediante array_column
.
Uso
$ordinals = [
''first'' => ''1st'',
''second'' => ''2nd'',
''third'' => ''3rd'',
];
$func = function ($k, $v) {
return [''new '' . $k, ''new '' . $v];
};
var_dump(array_map_assoc($func, $ordinals));
Salida
array(3) {
["new first"]=>
string(7) "new 1st"
["new second"]=>
string(7) "new 2nd"
["new third"]=>
string(7) "new 3rd"
}
Aplicación parcial
En caso de que necesite usar la función muchas veces con diferentes matrices pero con la misma función de mapeo, puede hacer algo llamado aplicación de función parcial (relacionada con '' currying ''), que le permite pasar solo el conjunto de datos al invocarlo:
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));
Que produce el mismo resultado, dado $func
y $ordinals
son como antes.
NOTA: si la función asignada devuelve la misma clave para dos entradas diferentes, el valor asociado con la clave posterior ganará. Invierta la matriz de entrada y el resultado de salida de array_map_assoc
para permitir que las teclas anteriores ganen. (Las claves devueltas en mi ejemplo no pueden colisionar ya que incorporan la clave de la matriz fuente, que a su vez debe ser única).
Alternativa
Lo que sigue es una variante de lo anterior, que podría ser más lógico para algunos, pero requiere PHP 5.6:
function array_map_assoc(callable $f, array $a) {
return array_merge(...array_map($f, array_keys($a), $a));
}
En esta variante, la función suministrada (sobre la que se mapea la matriz de datos) debería devolver una matriz asociativa con una fila, es decir, return [key => value]
. El resultado de mapear el llamable simplemente se desempaqueta y pasa a array_merge
. Como antes, al devolver una clave duplicada se obtendrán valores posteriores.
Si está en PHP 5.3 a 5.5, lo siguiente es equivalente. Utiliza array_reduce
y el operador binario +
array para convertir la matriz bidimensional resultante en una matriz unidimensional conservando las claves:
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;
}, []);
}
Uso
Ambas variantes se usarían así:
$ordinals = [
''first'' => ''1st'',
''second'' => ''2nd'',
''third'' => ''3rd'',
];
$func = function ($k, $v) {
return [''new '' . $k => ''new '' . $v];
};
var_dump(array_map_assoc($func, $ordinals));
Tenga en cuenta que =>
lugar de ,
en $func
.
El resultado es el mismo que antes, y cada uno se puede aplicar parcialmente de la misma manera que antes.
Resumen
El objetivo de la pregunta original es hacer que la invocación de la llamada sea lo más simple posible, a expensas de tener una función más complicada que se invoca; especialmente, para tener la capacidad de pasar el conjunto de datos como un único argumento, sin dividir las claves y valores. Usando la función suministrada al comienzo de esta respuesta:
$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)));
O bien, solo para esta pregunta, podemos simplificar la función array_map_assoc()
que elimina las claves de salida, ya que la pregunta no las solicita:
$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));
Entonces, la respuesta es NO , no puede evitar llamar a array_keys
, pero puede abstraer el lugar donde se llama a array_keys
en una función de orden superior, que podría ser lo suficientemente buena.
¿Hay alguna manera de hacer algo como esto?
$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)));
¿Pero en vez de llamar a array_keys
y array_values
, pasando directamente la variable $test_array
?
El resultado deseado es:
array(2) {
[0]=>
string(27) "first_key loves first_value"
[1]=>
string(29) "second_key loves second_value"
}
Así es como lo he implementado en mi proyecto.
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));
}
Basado en la respuesta de eis , esto es lo que finalmente hice para evitar ensuciar la matriz original:
$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);
Con PHP5.3 o posterior:
$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)
)
);
Esta es probablemente la forma más rápida y fácil de razonar:
$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'')
)
Hice esta función, basada en la respuesta 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;
}
Ejemplo:
$test_array = array("first_key" => "first_value",
"second_key" => "second_value");
var_dump(array_map_(function($key, $value){
return $key . " loves " . $value;
}, $arr));
Salida:
array (
''first_key'' => ''first_key loves first_value,
''second_key'' => ''second_key loves second_value'',
)
Por supuesto, puede usar array_values
para devolver exactamente lo que OP quiere.
array_values(array_map_(function($key, $value){
return $key . " loves " . $value;
}, $test_array))
No con array_map, ya que no maneja las claves.
array_walk hace:
$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);
Sin embargo, cambia la matriz dada como parámetro, por lo que no es exactamente una programación funcional (ya que tiene la pregunta etiquetada de esa manera).
Podrías escribir una función como esa si quisieras.
Por "ciclo manual" quise decir escribir una función personalizada que utiliza foreach
. Esto devuelve una nueva matriz como array_map
porque el alcance de la función hace que $array
sea una copia, no una referencia:
function map($array, callable $fn) {
foreach ($array as $k => &$v) $v = call_user_func($fn, $k, $v);
return $array;
}
Su técnica usando array_map
con array_keys
aunque parece más simple, es más poderosa porque puede usar null
como devolución de llamada para devolver los pares clave-valor:
function map($array, callable $fn = null) {
return array_map($fn, array_keys($array), $array);
}
Siempre me gusta la variante de JavaScript del mapa de la matriz. La versión más simple de esto sería:
/**
* @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;
}
Entonces ahora puedes pasarle una función de devolución de llamada para construir los valores.
$testArray = [
"first_key" => "first_value",
"second_key" => "second_value"
];
var_dump(
arrayMap($testArray, function($value, $key) {
return $key . '' loves '' . $value;
});
);
Veo que falta la respuesta obvia:
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;
}
Funciona exactamente como array_map. Casi.
En realidad, no es un map
puro como lo conoces de otros idiomas. Php es muy raro, por lo que requiere algunas funciones de usuario muy extrañas, ya que no queremos que se rompa, nuestro worse is better
enfoque.
Realmente, en realidad no es un map
en absoluto. Sin embargo, sigue siendo muy útil.
La primera diferencia obvia de array_map es que la devolución de llamada toma salidas de
each()
de cada matriz de entrada en lugar de valor solo. Todavía puede iterar a través de más matrices a la vez.La segunda diferencia es la forma en que se maneja la clave después de que se devuelve desde la devolución de llamada; el valor de retorno de la función de devolución de llamada debe ser una
array(''new_key'', ''new_value'')
. Las teclas pueden y serán cambiadas, las mismas teclas pueden incluso sobrescribir el valor anterior, si se devolvió la misma clave. Este no es un comportamiento demap
común, pero le permite reescribir claves.La tercera cosa extraña es que si omite la
key
en el valor de retorno (ya sea porarray(1 => ''value'')
oarray(null, ''value'')
), se asignará una nueva clave, como si$array[] = $value
fue utilizado. Tampoco es el comportamiento común delmap
, pero a veces resulta útil, supongo.En cuarto lugar, lo extraño es que si la función de devolución de llamada no devuelve un valor o devuelve un valor
null
, se omite todo el conjunto de claves y valores actuales de la salida, simplemente se omite. Esta característica es totalmente unmap
py, sin embargo, haría que esta función sea excelente. Doble truco paraarray_filter_assoc
, si existiera tal función.Si omite el segundo elemento (
1 => ...
) (la parte de valor ) en el retorno de devolución de llamada, se usanull
lugar de valor real.Cualquier otro elemento excepto aquellos con las claves
0
y1
en retorno de devolución de llamada se ignoran.Y, por último, si lambda devuelve cualquier valor excepto
null
o array, se trata como si se omitieran tanto la clave como el valor, así que:- se asigna una nueva clave para el elemento
-
null
se usa como valor
ADVERTENCIA:
Tenga en cuenta que esta última característica es solo un residuo de las características anteriores y que probablemente sea completamente inútil. Se desaconseja confiar en esta característica, ya que esta característica se desaprobará aleatoriamente y se cambiará inesperadamente en futuras versiones.
NOTA:
A diferencia de array_map
, todos los parámetros que no son de matriz pasados a array_map_assoc
, con la excepción del primer parámetro de devolución de llamada, se envían silenciosamente a las matrices.
EJEMPLOS:
// TODO: examples, anyone?
YaLinqo biblioteca YaLinqo * es adecuada para este tipo de tareas. Es un puerto de LINQ de .NET que admite completamente valores y claves en todas las devoluciones de llamada y se asemeja a SQL. Por ejemplo:
$mapped_array = from($test_array)
->select(function ($v, $k) { return "$k loves $v"; })
->toArray();
o solo:
$mapped_iterator = from($test_array)->select(''"$k loves $v"'');
Aquí, ''"$k loves $v"''
es un atajo para la sintaxis de cierre completo que esta biblioteca admite. toArray()
al final es opcional. La cadena de método devuelve un iterador, por lo que si el resultado solo necesita toArray
usando foreach
, se puede eliminar la llamada de toArray
.
* desarrollado por mí