Ответ 1
function transpose($array) {
array_unshift($array, null);
return call_user_func_array('array_map', $array);
}
Или, если вы используете PHP 5.6 или новее:
function transpose($array) {
return array_map(null, ...$array);
}
Как бы вы перевернули 90-градусный (транспонированный) многомерный массив в PHP? Например:
// Start with this array
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2',
3 => 'a3'
),
'b' => array(
1 => 'b1',
2 => 'b2',
3 => 'b3'
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
$bar = flipDiagonally($foo); // Mystery function
var_dump($bar[2]);
// Desired output:
array(3) {
["a"]=>
string(2) "a2"
["b"]=>
string(2) "b2"
["c"]=>
string(2) "c2"
}
Как бы вы реализовали flipDiagonally()
?
Изменить: это не домашнее задание. Я просто хочу посмотреть, есть ли у любого SOers более креативное решение, чем самый очевидный маршрут. Но так как некоторые люди жаловались на то, что эта проблема слишком простая, а что касается более общего решения, которое работает с массивом размеров n th?
то есть. Как вы напишете функцию так, чтобы:
$foo[j][k][...][x][y][z] = $bar[z][k][...][x][y][j]
? (ps. Я не думаю, что 12 вложенных for loops
- лучшее решение в этом случае.)
function transpose($array) {
array_unshift($array, null);
return call_user_func_array('array_map', $array);
}
Или, если вы используете PHP 5.6 или новее:
function transpose($array) {
return array_map(null, ...$array);
}
С 2 циклами.
function flipDiagonally($arr) {
$out = array();
foreach ($arr as $key => $subarr) {
foreach ($subarr as $subkey => $subvalue) {
$out[$subkey][$key] = $subvalue;
}
}
return $out;
}
Я думаю, вы имеете в виду массив transpose (столбцы становятся строками, строки становятся столбцами).
Вот функция, которая делает это для вас (источник):
function array_transpose($array, $selectKey = false) {
if (!is_array($array)) return false;
$return = array();
foreach($array as $key => $value) {
if (!is_array($value)) return $array;
if ($selectKey) {
if (isset($value[$selectKey])) $return[] = $value[$selectKey];
} else {
foreach ($value as $key2 => $value2) {
$return[$key2][$key] = $value2;
}
}
}
return $return;
}
Транспонирование N-мерного массива:
function transpose($array, &$out, $indices = array())
{
if (is_array($array))
{
foreach ($array as $key => $val)
{
//push onto the stack of indices
$temp = $indices;
$temp[] = $key;
transpose($val, $out, $temp);
}
}
else
{
//go through the stack in reverse - make the new array
$ref = &$out;
foreach (array_reverse($indices) as $idx)
$ref = &$ref[$idx];
$ref = $array;
}
}
$foo[1][2][3][3][3] = 'a';
$foo[4][5][6][5][5] = 'b';
$out = array();
transpose($foo, $out);
echo $out[3][3][3][2][1] . ' ' . $out[5][5][6][5][4];
Действительно взломать и, вероятно, не лучшее решение, но эй это работает.
В основном, он рекурсивно пересекает массив, накапливая текущие знаки в массиве.
Как только он достигает ссылочного значения, он принимает "стек" индексов и меняет его, помещая его в массив $out. (Есть ли способ избежать использования массива $temp?)
Я столкнулся с той же проблемой. Вот что я придумал:
function array_transpose(array $arr)
{
$keys = array_keys($arr);
$sum = array_values(array_map('count', $arr));
$transposed = array();
for ($i = 0; $i < max($sum); $i ++)
{
$item = array();
foreach ($keys as $key)
{
$item[$key] = array_key_exists($i, $arr[$key]) ? $arr[$key][$i] : NULL;
}
$transposed[] = $item;
}
return $transposed;
}
Мне нужна была функция транспонирования с поддержкой ассоциативного массива:
$matrix = [
['one' => 1, 'two' => 2],
['one' => 11, 'two' => 22],
['one' => 111, 'two' => 222],
];
$result = \array_transpose($matrix);
$trans = [
'one' => [1, 11, 111],
'two' => [2, 22, 222],
];
И обратно:
$matrix = [
'one' => [1, 11, 111],
'two' => [2, 22, 222],
];
$result = \array_transpose($matrix);
$trans = [
['one' => 1, 'two' => 2],
['one' => 11, 'two' => 22],
['one' => 111, 'two' => 222],
];
Тройка array_unshift
не работает NOR array_map
...
Итак, я закодировал функцию array_map_join_array
для связи с ассоциацией ключей записей:
/**
* Similar to array_map() but tries to join values on intern keys.
* @param callable $callback takes 2 args, the intern key and the list of associated values keyed by array (extern) keys.
* @param array $arrays the list of arrays to map keyed by extern keys NB like call_user_func_array()
* @return array
*/
function array_map_join_array(callable $callback, array $arrays)
{
$keys = [];
// try to list all intern keys
array_walk($arrays, function ($array) use (&$keys) {
$keys = array_merge($keys, array_keys($array));
});
$keys = array_unique($keys);
$res = [];
// for each intern key
foreach ($keys as $key) {
$items = [];
// walk through each array
array_walk($arrays, function ($array, $arrKey) use ($key, &$items) {
if (isset($array[$key])) {
// stack/transpose existing value for intern key with the array (extern) key
$items[$arrKey] = $array[$key];
} else {
// or stack a null value with the array (extern) key
$items[$arrKey] = null;
}
});
// call the callback with intern key and all the associated values keyed with array (extern) keys
$res[$key] = call_user_func($callback, $key, $items);
}
return $res;
}
и array_transpose
стало очевидным:
function array_transpose(array $matrix)
{
return \array_map_join_array(function ($key, $items) {
return $items;
}, $matrix);
}
Используйте это как
<?php
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2',
3 => 'a3'
),
'b' => array(
1 => 'b1',
2 => 'b2',
3 => 'b3'
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
echo "<pre>";
$i=0;
foreach ($foo as $val)
{ $i++;
$array[$i] = array_column($foo, $i);
}
print_r($array);
?>
Результат:
Array
(
[1] => Array
(
[0] => a1
[1] => b1
[2] => c1
)
[2] => Array
(
[0] => a2
[1] => b2
[2] => c2
)
[3] => Array
(
[0] => a3
[1] => b3
[2] => c3
)
)
<?php
$tableau_init = [
[
"prenom" => "med",
"age" => 1
],
[
"prenom" => "hassan",
"age" => 2
],
[
"prenom" => "ali",
"age" => 3
]
];
function transpose($tableau){
$out = array();
foreach ($tableau as $key => $value){
foreach ($value as $subKey => $subValue){
$out[$subKey][$key] = $subValue;
}
}
echo json_encode($out);
}
transpose($tableau_init);
Попробуйте понравиться
Прежде чем начать, я хотел бы снова сказать спасибо @quazardus для публикации своего обобщенного решения для трансляции любых двух размерных ассоциативных (или неассоциативных) массивов!
Как я обычно привык писать свой код как можно более подробно, я продолжил "сведение к минимуму" его кода немного дальше. Это, скорее всего, будет не для всех. Но на всякий случай кто-то должен быть заинтересован, вот мое решение:
function arrayMap($cb, array $arrays) // $cb: optional callback function
{ $keys = [];
array_walk($arrays, function ($array) use (&$keys)
{ $keys = array_merge($keys, array_keys($array)); });
$keys = array_unique($keys); $res = [];
foreach ($keys as $key) {
$items = array_map(function ($arr) use ($key)
{return isset($arr[$key]) ? $arr[$key] : null; },$arrays);
$res[$key] = call_user_func(
is_callable($cb) ? $cb
: function($k, $itms){return $itms;},
$key, $items);
}
return $res;
}
Теперь, аналогично стандартной функции PHP array_map()
, когда вы вызываете
arrayMap(null,$b);
вы получите желаемую транспонированную матрицу.
Это еще один способ сделать то же самое, что и ответ @codler. Мне пришлось сбросить некоторые массивы в CSV, поэтому я использовал следующую функцию:
function transposeCsvData($data)
{
$ct=0;
foreach($data as $key => $val)
{
//echo count($val);
if($ct< count($val))
$ct=count($val);
}
//echo $ct;
$blank=array_fill(0,$ct,array_fill(0,count($data),null));
//print_r($blank);
$retData = array();
foreach ($data as $row => $columns)
{
foreach ($columns as $row2 => $column2)
{
$retData[$row2][$row] = $column2;
}
}
$final=array();
foreach($retData as $k=>$aval)
{
$final[]=array_replace($blank[$k], $aval);
}
return $final;
}
Тест и ссылка на выход: https://tutes.in/how-to-transpose-an-array-in-php-with-irregular-subarray-size/
Вот способ array_walk для достижения этой цели,
function flipDiagonally($foo){
$temp = [];
array_walk($foo, function($item,$key) use(&$temp){
foreach($item as $k => $v){
$temp[$k][$key] = $v;
}
});
return $temp;
}
$bar = flipDiagonally($foo); // Mystery function
Если вы попытаетесь распаковать пример данных OP с помощью оператора splat (...
), вы сгенерируете:
Фатальная ошибка: Uncaught Ошибка: невозможно распаковать массив с ключами строки
Чтобы преодолеть эту ошибку, вызовите array_values()
для индексации ключей первого уровня перед распаковкой.
var_export(array_map(null, ...array_values($foo)));
Выход:
array (
0 =>
array (
0 => 'a1',
1 => 'b1',
2 => 'c1',
),
1 =>
array (
0 => 'a2',
1 => 'b2',
2 => 'c2',
),
2 =>
array (
0 => 'a3',
1 => 'b3',
2 => 'c3',
),
)
Дополнительная особенность/неожиданность в отношении транспонирования с помощью этой техники заключается в том, что null
элементы будут генерироваться, когда подмассивы имеют разные размеры... но, возможно, не там, где вы могли бы ожидать.
Из примеров данных, как это:
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2'
),
'b' => array(
1 => 'b1',
3 => 'b3'
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
Выход:
array (
0 =>
array (
0 => 'a1',
1 => 'b1',
2 => 'c1',
),
1 =>
array (
0 => 'a2',
1 => 'b3',
2 => 'c2',
),
2 =>
array (
0 => NULL,
1 => NULL,
2 => 'c3',
),
)
Обратите внимание на уровень заботы, проявляемый этой функцией (сравнимый с обработчиками багажа, которые вывозят ваш багаж из живота самолета). Не обращают внимания на идентификаторы исходных значений подмассива (и не имеет значения, если 1
, 2
, & 3
были x
, y
, & z
); все, что сходит с конвейерной ленты, выбрасывается в самый нижний слот.
Такое поведение является последовательным и надежным в предоставлении полной матрицы. Альтернатива цикла foreach()
не будет изначально доставлять null
элемент из подмассивов разных размеров, и в большинстве реализаций его возможность доступа ко всем значениям подмассива зависит от длины первого подмассива.
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2'
),
'b' => array(
1 => 'b1',
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
foreach (current($foo) as $column => $not_used) {
$result[] = array_column($foo, $column);
}
var_export($result);
Выход:
array (
0 =>
array (
0 => 'a1',
1 => 'b1',
2 => 'c1',
),
1 =>
array (
0 => 'a2',
1 => 'c2',
),
)
Как показано выше, если вы хотите быть уверены, что извлекли ВСЕ данные из входного массива, вам нужно написать логику сложения, чтобы доставить все уникальные идентификаторы столбцов в цикл foreach.
До того, как я узнал об этом сокращенном синтаксисе транспонирования, я написал более уродливый, более подробный функциональный транспозитор, который выдержал некоторую критику.
Вот вариант решения Кодлера/Андреаса, который работает с ассоциативными массивами. Несколько длиннее, но без петель:
<?php
function transpose($array) {
$keys = array_keys($array);
return array_map(function($array) use ($keys) {
return array_combine($keys, $array);
}, array_map(null, ...array_values($array)));
}
Пример:
<?php
$foo = array(
"fooA" => [ "a1", "a2", "a3"],
"fooB" => [ "b1", "b2", "b3"],
"fooC" => [ "c1", "c2", "c3"]
);
print_r( $transpose( $foo ));
// Output like this:
Array (
[0] => Array (
[fooA] => a1
[fooB] => b1
[fooC] => c1
)
[1] => Array (
[fooA] => a2
[fooB] => b2
[fooC] => c2
)
[2] => Array (
[fooA] => a3
[fooB] => b3
[fooC] => c3
)
);