web-dev-qa-db-de.com

Transponieren mehrdimensionaler Arrays in PHP

Wie würden Sie ein mehrdimensionales Array in PHP um 90 Grad kippen (transponieren)? Zum Beispiel:

// 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"
}

Wie würden Sie flipDiagonally() implementieren?

Edit: Das ist keine Hausaufgabe. Ich möchte nur sehen, ob ein SOer eine kreativere Lösung als der naheliegendste Weg hat. Aber da sich einige Leute darüber beschwert haben, dass dieses Problem zu einfach ist, gibt es eine allgemeinere Lösung, die mit einem n funktioniertth Dimensionsarray?

wie würden Sie eine Funktion schreiben, so dass:

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

(ps. Ich glaube nicht, dass 12 verschachtelte for loops in diesem Fall die beste Lösung ist.)

65
Calvin
function transpose($array) {
    array_unshift($array, null);
    return call_user_func_array('array_map', $array);
}

Oder wenn Sie PHP 5.6 oder höher verwenden:

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

Mit 2 Schlaufen.

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

Ich denke, Sie beziehen sich auf das Array transpose (Spalten werden zu Zeilen, Zeilen werden zu Spalten).

Hier ist eine Funktion, die es für Sie tut (Quelle) :

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

Transponieren eines N-dimensionalen Arrays:

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

Wirklich hackig und wahrscheinlich nicht die beste Lösung, aber es funktioniert.

Grundsätzlich durchläuft es das Array rekursiv und akkumuliert die aktuellen Anzeigen in einem Array.
Wenn es zu dem referenzierten Wert kommt, nimmt er den "Stapel" der Indizes und kehrt ihn um, indem er ihn in das $ out-Array legt. (Gibt es eine Möglichkeit, die Verwendung des $ temp-Arrays zu vermeiden?)

2
v3.

Hier ist eine Variation von Codler/Andreas 'Lösung , die mit assoziativen Arrays funktioniert. Etwas länger, aber ohne Schleife:

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

Beispiel:

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

Ich wurde mit dem gleichen Problem konfrontiert. Folgendes habe ich mir ausgedacht: 

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;
}
1
José Trindade

Ich brauchte eine Transponierungsfunktion mit Unterstützung für assoziatives 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],
    ];

Und der Weg zurück:

    $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],
    ];

Der array_unshift-Trick funktionierte nicht NOR der array_map...

Ich habe also eine array_map_join_array-Funktion für die Zuordnung von Datensatzschlüsseln codiert:

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

und array_transpose wurde offensichtlich:

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

Verwenden Sie so

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

?>

Ergebnis:

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
        )

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

Versuchen Sie es so

0
MrCharif

Wenn Sie versuchen, die Beispieldaten des OP mit dem splat-Operator (...) zu entpacken, werden Sie Folgendes generieren:

Schwerwiegender Fehler: Nicht erfasster Fehler: Array kann nicht mit Zeichenfolgenschlüsseln entpackt werden

Beweis

Um diesen Fehler zu beheben, rufen Sie array_values() auf, um die Schlüssel der ersten Ebene vor dem Auspacken zu indizieren.

var_export(array_map(null, ...array_values($foo)));

Ausgabe:

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',
  ),
)

Eine zusätzliche Funktion/Überraschung beim Transponieren mit dieser Technik ist, dass null Elemente generiert werden, wenn die Subarrays unterschiedlich groß sind ... aber möglicherweise nicht dort, wo Sie dies erwarten.

Aus Beispieldaten wie folgt:

$foo = array(
    'a' => array(
       1 => 'a1',
       2 => 'a2'
    ),
    'b' => array(
       1 => 'b1',
       3 => 'b3' 
    ),
    'c' => array(
       1 => 'c1',
       2 => 'c2',
       3 => 'c3' 
    )
);

Die Ausgabe ist:

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',
  ),
)

Beachten Sie die Sorgfalt der Funktion (vergleichbar mit den Gepäckabfertigern, die Ihr Gepäck aus dem Bauch des Flugzeugs nehmen). Die IDs der ursprünglichen Subarray-Werte werden nicht berücksichtigt (und es spielt keine Rolle, ob 1, 2, & 3 eine x, y, & z wäre). Was auch immer vom Förderband kommt, wird in den niedrigsten verfügbaren Schlitz geworfen.

Dieses Verhalten ist bei der Bereitstellung einer vollständigen Matrix konsistent und zuverlässig. Eine foreach()-Schleifenalternative liefert null -Elemente von Subarrays unterschiedlicher Größe nicht automatisch, und in den meisten Implementierungen hängt ihre Fähigkeit, auf alle Subarray-Werte zuzugreifen, von der Länge des ersten Subarrays ab.

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

Ausgabe:

array (
  0 => 
  array (
    0 => 'a1',
    1 => 'b1',
    2 => 'c1',
  ),
  1 => 
  array (
    0 => 'a2',
    1 => 'c2',
  ),
)

Wenn Sie wie oben gezeigt sicherstellen möchten, dass Sie ALLE Daten aus dem Eingabearray extrahiert haben, müssen Sie eine Additionslogik schreiben, um alle eindeutigen Spalten-IDs an die foreach-Schleife zu übergeben.


p.s. Bevor ich von dieser kurzen transponierenden Syntax erfuhr, schrieb ich einen hässlicheren, ausführlicheren Transponierer, der einige Kritik auslöste .

0
mickmackusa

Bevor ich anfange, möchte ich noch einmal Dank an @quazardus sagen, dass er seine generalisierte Lösung zum Übertragen von zwei dimenionalen assoziativen (oder nicht assoziativen) Arrays veröffentlicht hat! 

Da ich es mir zur Gewohnheit gemacht habe, meinen Code so knapp wie möglich zu schreiben, fuhr ich fort, seinen Code ein wenig weiter zu "minimieren". Dies wird sehr wahrscheinlichnicht für jeden Geschmack sein. Aber für den Fall, dass jemand interessiert sein sollte, hier ist meine Lösung für seine Lösung:

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

Nun analog zur PHP Standardfunktion array_map() beim Aufruf

arrayMap(null,$b);

sie erhalten die gewünschte transponierte Matrix. 

0
cars10m

Hier ist array_walk Weg, um dies zu erreichen,

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 .

0
quickSwap

Dies ist eine weitere Möglichkeit, genau dasselbe zu tun, wie es die Antwort von @codler tut. Ich musste einige Arrays in csv sichern, also habe ich die folgende Funktion verwendet:

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- und Ausgabereferenz: https://tutes.in/how-to-transpose-an-array-in-php-with-irregular-subarray-size/

0
th3pirat3