Карта массива PHP, включая ключи
есть ли способ сделать что-то вроде этого:
$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)));
но вместо вызова array_keys
и array_values
, непосредственно проходя мимо $test_array
переменной?
требуется:
array(2) {
[0]=>
string(27) "first_key loves first_value"
[1]=>
string(29) "second_key loves second_value"
}
15 ответов
Не с array_map, так как он не обрабатывает ключи.
array_walk тут:
$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);
однако он изменяет массив, заданный как параметр, поэтому это не совсем функциональное программирование (поскольку у вас есть такой вопрос).
вы могли бы написать такую функцию самостоятельно, если бы захотели.
Это, вероятно, самый короткий и простой в рассуждении:
$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')
)
вот мое очень простое, PHP 5.5-совместимое решение:
function array_map_assoc(callable $f, array $a) {
return array_column(array_map($f, array_keys($a), $a), 1, 0);
}
вызываемый вами объект должен сам возвращать массив с двумя значениями, т. е. return [key, value]
. Внутренний призыв к array_map
поэтому создает массив массивов. Затем это преобразуется обратно в одномерный массив с помощью 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"
}
частичное применение
в случае, если вам нужно использовать функцию много раз с различные массивы, но та же функция отображения, вы можете сделать что-то под названием частичное применение функции (связанные с ‘карринг’), который позволяет передавать только массив данных при вызове:
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));
который производит тот же выход, учитывая $func
и $ordinals
как ранее.
Примечание: Если ваша сопоставленная функция возвращает такой же ключ для двух разных входов, значение, связанное с позже ключ победит. обратный входной массив и выходной результат array_map_assoc
разрешить ранее ключи к победе. (Возвращаемые ключи в моем примере не могут сталкиваться, поскольку они включают ключ исходного массива, который, в свою очередь, должен быть уникальным.)
альтернатива
Ниже приведен вариант выше, который может оказаться более логичным для некоторых, но требует PHP 5.6:
function array_map_assoc(callable $f, array $a) {
return array_merge(...array_map($f, array_keys($a), $a));
}
в этом варианте, ваша поставленная функция (над которой массив данных карту), а не должен возвращать ассоциативный массив с одной строкой, т. е. return [key => value]
.
Результат сопоставления вызываемого объекта затем просто распаковывается и передается в array_merge
. Как и ранее, возврат дубликата ключа приведет к выигрышу более поздних значений.
n.b. Alex83690 отметил в комментарии, что с помощью
array_replace
здесь вместоarray_merge
сохранит целочисленные ключи.array_replace
не изменяет входной массив, поэтому безопасен для функционального кода.
если вы находитесь на PHP 5.3 до 5.5, следующее эквивалентно. Он использует
С php5.3 или более поздняя версия:
$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)
)
);
YaLinqo библиотека* хорошо подходит для такого рода задач. Это порт LINQ из .NET, который полностью поддерживает значения и ключи во всех обратных вызовах и напоминает SQL. Например:
$mapped_array = from($test_array)
->select(function ($v, $k) { return "$k loves $v"; })
->toArray();
или так:
$mapped_iterator = from($test_array)->select('"$k loves $v"');
здесь '"$k loves $v"'
- это ярлык для полного синтаксиса закрытия, который поддерживает эта библиотека. toArray()
В конце не является обязательным. Цепочка методов возвращает итератор, поэтому, если результат просто должен быть повторен с помощью foreach
, toArray
звонок может быть удаленный.
* я
на основе ответ eis, вот что я в конечном итоге сделал, чтобы не испортить исходный массив:
$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);
под "ручным циклом" я имел в виду написать пользовательскую функцию, которая использует foreach
. Это возвращает новый массив, такой как array_map
делает, потому что область действия функции вызывает $array
быть копией - не ссылка:
function map($array, callable $fn) {
foreach ($array as $k => &$v) $v = call_user_func($fn, $k, $v);
return $array;
}
ваша техника использования array_map
С array_keys
хотя на самом деле кажется проще и мощнее, потому что вы можете использовать null
в качестве обратного вызова возвращает пары ключ-значение:
function map($array, callable $fn = null) {
return array_map($fn, array_keys($array), $array);
}
вот как я реализовал это в своем проекте.
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));
}
Я сделал эту функцию, на основе ответ 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;
}
пример:
$test_array = array("first_key" => "first_value",
"second_key" => "second_value");
var_dump(array_map_(function($key, $value){
return $key . " loves " . $value;
}, $arr));
выход:
array (
'first_key' => 'first_key loves first_value,
'second_key' => 'second_key loves second_value',
)
конечно, вы можете использовать array_values
чтобы вернуть именно то, что OP хочет.
array_values(array_map_(function($key, $value){
return $key . " loves " . $value;
}, $test_array))
я вижу, что не хватает очевидного ответа:
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;
}
работает точно так же, как array_map. Почти.
на самом деле, это не чисто map
Как вы знаете это из других языков. Php очень странный, поэтому он требует некоторых очень странных пользовательских функций, потому что мы не хотим разбивать наш точно сломанный worse is better
подход.
на самом деле, это не на самом деле map
на всех. Тем не менее, это все еще очень полезно.
первая очевидная разница из array_map, является то, что обратный вызов принимает выходы
each()
из каждого входного массива вместо одного значения. Вы все еще можете перебирать сразу несколько массивов.вторая разница-это способ обработки ключа после его возврата из обратного вызова; возвращаемое значение из функции обратного вызова должно быть
array('new_key', 'new_value')
. Ключи могут и будут изменены, те же ключи могут даже привести к перезаписи предыдущего значения, если тот же ключ был возвращен. Это не распространеноmap
поведение, но это позволяет переписывать ключи.третья странная вещь, если вы опускаете
key
в возвращаемом значении (либоarray(1 => 'value')
илиarray(null, 'value')
), новый ключ будет назначен, как будто . Это неmap
' ы обычное поведение либо, но это иногда пригодится, я думаю.четвертая странная вещь, если функция обратного вызова не возвращает значение или возвращает
null
, весь набор текущих ключей и значений опущен из выходных данных, его просто пропустили. Эта функция полностью неmap
py, но это сделало бы эту функцию отличной двойной трюк дляarray_filter_assoc
, если бы была такая функция.если вы опустите второй элемент (
1 => ...
) (the стоимостью часть) в обратном вызове,null
используется вместо реального значения.любые другие элементы, кроме тех, кто с ключами
0
и1
в возвращении обратного вызова игнорируемый.-
и, наконец, если лямбда возвращает любое значение, кроме
null
или массив, он рассматривается как если бы и ключ и значение были опущены, так что:- назначен новый ключ для элемента
-
null
используется как значение
Внимание:
Имейте в виду, что эта последняя функция-всего лишь остаток предыдущих функций, и она, вероятно, совершенно бесполезна. Опираясь на эту функцию очень обескуражен, так как эта функция будет случайным образом устаревший и неожиданно изменился в будущих выпусках.
Примечание:
В отличие от in array_map
, все параметры без массива передаются в array_map_assoc
, за исключением первого параметра обратного вызова, молча преобразован в массивы.
примеры:// TODO: examples, anyone?
мне всегда нравится javascript-вариант карты массива. Самая простая версия этого была бы:
/**
* @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;
}
теперь вы можете просто передать ему функцию обратного вызова, как построить значения.
$testArray = [
"first_key" => "first_value",
"second_key" => "second_value"
];
var_dump(
arrayMap($testArray, function($value, $key) {
return $key . ' loves ' . $value;
});
);
Я бы сделал что-то вроде этого:
<?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",
)));
?>
результаты:
array(2) {
[0]=>
string(27) "first_key loves first_value"
[1]=>
string(29) "second_key loves second_value"
}
Я добавлю еще одно решение проблемы, используя 5.6 или более поздней версии. Не знаю, более ли это эффективно, чем уже отличные решения (возможно, нет), но для меня это просто проще читать:
$myArray = [
"key0" => 0,
"key1" => 1,
"key2" => 2
];
array_combine(
array_keys($myArray),
array_map(
function ($intVal) {
return strval($intVal);
},
$myArray
)
);
используя strval()
в качестве примера функции в array_map
, это будет генерировать:
array(3) {
["key0"]=>
string(1) "0"
["key1"]=>
string(1) "1"
["key2"]=>
string(1) "2"
}
надеюсь, я не единственный, кто находит это довольно простым для понимания.
array_combine
создает key => value
массив из массива ключей и массива значений, остальное довольно понятно.
посмотреть здесь! Существует тривиальное решение!
function array_map2(callable $f, array $a)
{
return array_map($f, array_keys($a), $a);
}
как указано в вопросе, array_map
уже имеет точно необходимую функциональность. Другие ответы здесь серьезно усложняют вещи:array_walk
не работает.
использование
именно так, как вы ожидали бы от вашего примера:
$test_array = array("first_key" => "first_value",
"second_key" => "second_value");
var_dump(array_map2(function($a, $b) { return "$a loves $b"; }, $test_array));
Я нашел это статьи и это сделало трюк для меня, как с array_map em array_walk вы не можете этого сделать.
$variables = array_reduce($invoice['variables'], function ($result, $item) {
$result[$item['variable']] = $item['value'];
return $result;
}, array());
конечно, есть некоторые уловы, такие как дубликаты ключей, но если у вас нет этой проблемы, это сделает трюк.