可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
How would you flip 90 degrees (transpose) a multidimensional array in PHP? For example:
// 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"
}
How would you implement flipDiagonally()
?
Edit: this is not homework. I just want to see if any SOers have a more creative solution than the most obvious route. But since a few people have complained about this problem being too easy, what about a more general solution that works with an nth dimension array?
i.e. How would you write a function so that:
$foo[j][k][...][x][y][z] = $bar[z][k][...][x][y][j]
?(ps. I don't think 12 nested for loops
is the best solution in this case.)
回答1:
function transpose($array) {
array_unshift($array, null);
return call_user_func_array('array_map', $array);
}
Or if you're using PHP 5.6 or later:
function transpose($array) {
return array_map(null, ...$array);
}
回答2:
With 2 loops.
function flipDiagonally($arr) {
$out = array();
foreach ($arr as $key => $subarr) {
foreach ($subarr as $subkey => $subvalue) {
$out[$subkey][$key] = $subvalue;
}
}
return $out;
}
回答3:
I think you're referring to the array transpose (columns become rows, rows become columns).
Here is a function that does it for you (source):
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;
}
回答4:
Transposing an N-dimensional array:
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];
Really hackish, and probably not the best solution, but hey it works.
Basically it traverses the array recursively, accumulating the current indicies in an array.
Once it gets to the referenced value, it takes the "stack" of indices and reverses it, putting it into the $out array. (Is there a way of avoiding use of the $temp array?)
回答5:
I got confronted with the same problem. Here is what i came up with:
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;
}
回答6:
I needed a transpose function with support for associative array:
$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],
];
And the way back:
$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],
];
The array_unshift
trick did not work NOR the array_map
...
So I've coded a array_map_join_array
function to deal with record keys association:
/**
* 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;
}
and array_transpose
became obvious:
function array_transpose(array $matrix)
{
return \array_map_join_array(function ($key, $items) {
return $items;
}, $matrix);
}
回答7:
Here's a variation of Codler/Andreas's solution that works with associative arrays. Somewhat longer but loop-less:
<?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)));
}
Example:
<?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
)
);
回答8:
Use like this
<?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);
?>
Result:
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
)
)
回答9:
<?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);
Try Like This
回答10:
Before I start, I'd like to say thanks again to @quazardus for posting his generalised solution for tranposing any two dimenional associative (or non-associative) array!
As I am in the habit of writing my code as tersely as possible I went on to "minimizing" his code a little further. This will very likely not be to everybody's taste. But just in case anyone should be interested, here is my take on his solution:
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;
}
Now, analogous to the PHP standard function array_map()
, when you call
arrayMap(null,$b);
you will get the desired transposed matrix.
回答11:
This is another way to do the exact same thing which @codler s answer does. I had to dump some arrays in csv so I used the following function:
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;
}
Test and output reference: https://tutes.in/how-to-transpose-an-array-in-php-with-irregular-subarray-size/
回答12:
Here is array_walk way to achieve this,
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
Demo.
回答13:
If you try to unpack the OP's sample data with the splat operator (...
), you will generate:
Fatal error: Uncaught Error: Cannot unpack array with string keys
Proof
To overcome this error, call array_values()
to index the first level keys before unpacking.
var_export(array_map(null, ...array_values($foo)));
Output:
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',
),
)
An additional feature/surprise regarding transposing with this technique is that null
elements will be generated when the subarrays are of differing sizes ...but maybe not where you might expect.
From sample data like this:
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2'
),
'b' => array(
1 => 'b1',
3 => 'b3'
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
The output is:
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',
),
)
Notice the level care exhibited by the function (comparable to the baggage handlers who take your luggage out of the belly of the plane). There is no attention to the original subarray values' ids (and it wouldn't matter if 1
, 2
, & 3
were x
, y
, & z
); whatever comes off the conveyer belt gets thrown in the lowest available slot.
This behavior is consistent and reliable in delivering a complete matrix. A foreach()
loop alternative will not natively deliver null
element from subarrays of different sizes, and in most implementations its ability to access all subarray values depends on the length of the first subarray.
$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);
Output:
array (
0 =>
array (
0 => 'a1',
1 => 'b1',
2 => 'c1',
),
1 =>
array (
0 => 'a2',
1 => 'c2',
),
)
As shown above, if you wanted to be sure that you extracted ALL of the data from the input array, you'd have to write addition logic to deliver all unique column ids to the foreach loop.
p.s. before I learned of this shorthand transposing syntax, I wrote an uglier, more verbose functional transposer that copped some criticism.