在PHP中转置多维数组


74

您将如何在PHP中翻转90度(转置)多维数组?例如:

// 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()

编辑:这不是作业。我只想看看是否有SOer拥有比最明显的路线更具创意的解决方案。但是,既然有人抱怨这个问题太容易了,那么使用n维数组的更通用的解决方案呢?

即您将如何编写一个函数,以便:

$foo[j][k][...][x][y][z] = $bar[z][k][...][x][y][j]

?(附言。for loops在这种情况下,我不认为嵌套12是最好的解决方案。)


@Calvin我知道这是很多年前(11!),但是..您是否接受了任何答案?您是否注意到最流行的答案基本上是错误的,因为不支持单行[[1,2,... N]]?请查看插图的沙盒:sandbox.onlinephpfunctions.com/code/…–
Onkeltem

此外,splat运算符无法解包字符串键。错误的证明:3v4l.org/1WSQH ...糟糕,我只是意识到我是一年多以前在本页上回答的!
mickmackusa

Answers:


253
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
嗯...对我有用,但是我真的不明白为什么。我看到以$ array作为参数调用了array_map,而null是第一个参数。但是为什么array_map会这样呢?为什么在这里将null作为参数甚至可以?
雅各布·朗格

20
将NULL用作array_unshift的参数,该参数会在数组的开头添加一个值。因此,第一行插入NULL作为数组的第一个值。下一行调用$ array的所有条目作为参数的array_map。因此,它与调用array_map(NULL,$ array [0],$ array [1],$ array [2]等)相同。在array_map文档中有一个详细信息:“此函数的一个有趣用途是构造一个数组数组,可以通过使用NULL作为回调函数的名称来轻松执行该数组”
Jeremy Warne 2012年

14
如果索引的类型为String,则此函数不会保留索引。它返回带有数字索引的转置矩阵。在这种情况下,flipDiagonally函数可以正常工作。无论如何还是为了简单而
投票

5
当只有一行时,这种情况就会分解,例如transpose([[[1,2]])预期:[[1],[2]]实际:[1,2]
chris

3
应该是答案中的一些描述。
Awlad Liton

69

有2个循环。

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

17
尽管Codler的回答更为简洁,但我认为这实际上是更好的解决方法,因为这样可以更清楚地了解正在发生的事情。如果有编码经验而不是php专家的人来看看这两个答案,他们会立即理解这个答案的作用,但是必须阅读文档的精巧版才能遵循另一个答案。+1
hackartist 2012年

相反,我发现使用非数字键效果不佳。例如$test = array(array('a'=>1, 'b'=>2,'c'=>3), array(4,5,6), array(7,8,9));:它使用非数字键为每个值创建一个单元素数组。使用指定的数字键(例如$test = array(array(4,5,6), array(11=>1, 12=>2, 13=>3), array(7,8,9));),它会有些怪异。虽然从总体上来说这应该可行,但我认为我们需要更好的解决方案!
约翰·K(JohnK)

@JohnK [0] [1]和[2] [1]将变为[1] [0]和[1] [2]。翻转按键。我尝试了您的示例,它完全按预期工作。我不确定您的期望。
OIS,2014年

有没有人介绍这两种解决方案?如果count($ arr)真的很高,那么Codler的解决方案的扩展能力如何?
donquixote

1
@donquixote Codler的解决方案是错误的。不好 它不支持关联数组,并且在单行/列的平凡边缘情况下失败:[[a,b,...,z]]。必须贬低它,以免混淆人们。
Onkeltem '20

8

我认为您指的是数组转置(列变成行,行变成列)。

这是一个为您完成的功能(源)

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

3

转置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数组的方法?)


2

我需要支持关联数组的转置函数:

    $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把戏不起作用或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);
}

1

我遇到了同样的问题。这是我想出的:

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

该答案缺少其简单的英语解释。
mickmackusa

1

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

致命错误:未捕获错误:无法使用字符串键解压缩数组

证明

要解决此错误,请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',
  ),
)

请注意,该功能具有高水平的维护功能(与将行李从飞机腹中取出的行李处理人员相比)。有没有关注到原来的子阵值IDS(和它不会不要紧,如果12,和3分别为xy,和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',
  ),
)

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


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


1

这是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)));
}

例:

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

这不是“无环”,而只是“功能”,换句话说,它是在没有语言构造的情况下进行迭代的。
mickmackusa

1

我们可以通过使用两个foreach来做到这一点。遍历一个数组和另一个数组以创建新数组,
如下所示:

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

$newFoo = [];
foreach($foo as $a => $k){
   foreach($k as $i => $j){
       $newFoo[$i][]= $j;
   }
}

检查输出

echo "<pre>";
print_r($newFoo);
echo "</pre>";

0

在开始之前,我想再次感谢@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);

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


当然,还有更多直接/简洁/高效的方法来收集唯一的第二级密钥。例如:$keys = array_keys(array_merge(...array_values($arrays)));一种语言构造将具有更少的复杂性甚至更好的性能。
mickmackusa

0

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


0

这是实现此目的的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

演示


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

像这样尝试


1
这看起来像是@OIS解决方案的无法解释的精确副本。这个答案没有附加值。
mickmackusa
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.