在 PHP 中转置多维数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/797251/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 23:53:15  来源:igfitidea点击:

Transposing multidimensional arrays in PHP

phparrayshigher-order-functions

提问by Calvin

How would you flip 90 degrees (transpose) a multidimensional array in PHP? For example:

你将如何翻转 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"
}

How would you implement flipDiagonally()?

你会如何实施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 nthdimension array?

编辑:这不是作业。我只是想看看是否有任何 SOers 有比最明显的路线更有创意的解决方案。但是由于一些人抱怨这个问题太简单了,那么一个适用于n维数组的更通用的解决方案呢?

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 loopsis the best solution in this case.)

?(ps。我不认为 12 个嵌套for loops是这种情况下的最佳解决方案。)

回答by Codler

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:

或者,如果您使用的是 PHP 5.6 或更高版本:

function transpose($array) {
    return array_map(null, ...$array);
}

回答by OIS

With 2 loops.

有 2 个循环。

function flipDiagonally($arr) {
    $out = array();
    foreach ($arr as $key => $subarr) {
        foreach ($subarr as $subkey => $subvalue) {
            $out[$subkey][$key] = $subvalue;
        }
    }
    return $out;
}

回答by Aziz

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;
} 

回答by v3.

Transposing an N-dimensional array:

转置 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];

Really hackish, and probably not the best solution, but hey it works.

真的很hackish,可能不是最好的解决方案,但嘿它有效。

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?)

基本上它递归地遍历数组,在数组中累积当前索引。
一旦到达引用的值,它就会获取索引的“堆栈”并将其反转,将其放入 $out 数组中。(有没有办法避免使用 $temp 数组?)

回答by quazardous

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_unshifttrick did not work NOR the array_map...

这个array_unshift技巧没有奏效,也没有array_map......

So I've coded a array_map_join_arrayfunction to deal with record keys association:

所以我编写了一个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;
}

and array_transposebecame obvious:

array_transpose变得显而易见:

function array_transpose(array $matrix)
{
    return \array_map_join_array(function ($key, $items) {
        return $items;
    }, $matrix);
}

回答by José Trindade

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;
}

回答by mickmackusa

If you try to unpack the OP's sample data with the splat operator (...), you will generate:

如果您尝试使用 splat 运算符 ( ...)解压缩 OP 的示例数据,您将生成:

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.

为了克服这个错误,array_values()在解包之前调用索引第一级键。

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 nullelements will be generated when the subarrays are of differing sizes ...but maybe not where you might expect.

使用此技术转置的另一个功能/惊喜是,null当子数组的大小不同时,将生成元素……但可能不是您所期望的。

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, & 3were x, y, & z); whatever comes off the conveyer belt gets thrown in the lowest available slot.

请注意该功能表现出的水平护理(与将行李从飞机腹部取出的行李搬运工相比)。没有注意原始子数组值的 id(如果1, 2, &3x, y, &则无关紧要z);从传送带上掉下来的任何东西都会被扔到最低的可用槽中。

This behavior is consistent and reliable in delivering a complete matrix. A foreach()loop alternative will not natively deliver nullelement from subarrays of different sizes, and in most implementations its ability to access all subarray values depends on the length of the first subarray.

这种行为在提供完整矩阵时是一致且可靠的。甲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);

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.

如上所示,如果您想确保从输入数组中提取了所有数据,则必须编写加法逻辑以将所有唯一的列 ID 传递给 foreach 循环。



p.s. before I learned of this shorthand transposing syntax, I wrote an uglier, more verbose functional transposer that copped some criticism.

ps 在我了解这种速记转置语法之前,我写了一个更丑陋、更冗长的功能转置器,以应对一些批评

回答by tomkyle

Here's a variation of Codler/Andreas's solutionthat works with associative arrays. Somewhat longer but loop-lesspurely functional:

这是适用于关联数组的Codler/Andreas 解决方案的变体。有点长但是无环纯功能:

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

回答by cars10m

Before I start, I'd like to say thanksagain to @quazardus for posting his generalised solution for tranposing any two dimenional associative (or non-associative) array!

在开始之前,我想再次感谢@quazardus 发布他的通用解决方案,用于转置任何二维关联(或非关联)数组!

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 likelynotbe 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

现在,类似于 PHP 标准函数array_map(),当您调用

arrayMap(null,$b);

you will get the desired transposed matrix.

您将获得所需的转置矩阵。

回答by th3pirat3

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:

这是另一种与@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;
    }

Test and output reference: https://tutes.in/how-to-transpose-an-array-in-php-with-irregular-subarray-size/

测试和输出参考:https: //tutes.in/how-to-transpose-an-array-in-php-with-irregular-subarray-size/