Массив массива 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 переменную $test_array ?

Желаемый результат:

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

Related of "Массив массива PHP, включая ключи"

Не с 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') ) 

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

Вот мое очень простое, совместимое с 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" } 

Карринг

Если вам нужно многократно использовать функцию с разными массивами, но с той же функцией сопоставления, вы можете сделать что-то, называемое « currying », которое позволяет вам только передавать массив данных при вызове:

 function array_map_assoc_curried(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_curried($func); var_dump($my_mapping($ordinals)); 

Что дает тот же результат, что и $func и $ordinals , как раньше.

ПРИМЕЧАНИЕ. Если ваша отображаемая функция возвращает один и тот же ключ для двух разных входов, значение, связанное с более поздним ключом, будет выиграно. array_map_assoc входной массив и выведите результат 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 . Как и раньше, возврат дублирующего ключа приведет к получению более поздних значений.

Если вы используете PHP с 5.3 по 5.5, то это эквивалентно. Он использует array_reduce и оператор binary + array для преобразования полученного двумерного массива в одномерный массив при сохранении ключей:

 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; }, []); } 

Применение

Оба этих варианта будут использоваться таким образом:

 $ordinals = [ 'first' => '1st', 'second' => '2nd', 'third' => '3rd', ]; $func = function ($k, $v) { return ['new ' . $k => 'new ' . $v]; }; var_dump(array_map_assoc($func, $ordinals)); 

Обратите внимание на => вместо , в $func .

Вывод такой же, как и раньше, и каждый может быть выполнен так же, как и раньше.


Резюме

Цель первоначального вопроса состоит в том, чтобы сделать вызов вызова максимально простым, за счет использования более сложной функции, которая вызывается; особенно, чтобы иметь возможность передавать массив данных в виде одного аргумента без разделения ключей и значений. Используя функцию, поставленную в начале этого ответа:

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

Или, только для этого вопроса, мы можем упростить array_map_assoc() которая выводит выходные ключи, поскольку вопрос не запрашивает их:

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

Таким образом, ответ НЕТ , вы не можете избежать вызова array_keys , но можете абстрагироваться от места, где array_keys в функцию более высокого порядка, что может быть достаточно хорошим.

Основываясь на ответе 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); } 

Библиотека 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 :

 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; } с 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') ), новый ключ будет назначен, как если бы $array[] = $value . Это тоже не обычное поведение map , но иногда это бывает удобно, я думаю.

  • Четвертая странность заключается в том, что если функция обратного вызова не возвращает значение или возвращает значение null , весь набор текущих ключей и значений опускается на выходе, он просто пропускается. Эта функция полностью un map py, но это сделало бы эту функцию отличным stunt double для array_filter_assoc , если бы была такая функция.

  • Если вы опускаете второй элемент ( 1 => ... ) (часть значения ) в обратном null вместо реального значения используется значение null .

  • Любые другие элементы, кроме клавиш с 0 и 1 в обратном вызове, игнорируются.

  • И, наконец, если лямбда возвращает любое значение, кроме null или массива, оно обрабатывается так, как если бы оба ключа и значение были опущены, так что:

    1. назначается новый ключ для элемента
    2. null используется как значение

ПРЕДУПРЕЖДЕНИЕ:
Имейте в виду, что эта последняя особенность – всего лишь остаток предыдущих функций, и это, вероятно, совершенно бесполезно. Опираясь на эту особенность, она очень обескуражена, так как эта функция будет непредсказуемо изменена и неожиданно изменена в будущих выпусках.

ЗАМЕТКА:
В отличие от array_map , все параметры без массива, переданные в array_map_assoc , за исключением первого параметра обратного вызова, незаметно передаются в массивы.

ПРИМЕРЫ:
// TODO: examples, anyone?