web-dev-qa-db-fra.com

Transposition de tableaux multidimensionnels en PHP

Comment inverseriez-vous 90 degrés (transposons) un tableau multidimensionnel en PHP? Par exemple:

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

Comment mettriez-vous en œuvre flipDiagonally()?

Edit: ce n'est pas un devoir. Je veux juste voir si les SO ont une solution plus créative que la voie la plus évidente. Mais puisque quelques personnes se sont plaintes de ce problème trop facile, qu’en est-il d’une solution plus générale qui fonctionne avecth tableau de dimension?

comment écririez-vous une fonction pour que:

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

? ps. Je ne pense pas que 12 for loops imbriqué soit la meilleure solution dans ce cas.)

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

Ou si vous utilisez PHP 5.6 ou une version ultérieure:

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

Avec 2 boucles.

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

Je pense que vous faites référence au tableau transpose (les colonnes deviennent des lignes, les lignes deviennent des colonnes).

Voici une fonction qui le fait pour vous (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;
} 
8
Aziz

Transposer un tableau à N dimensions:

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

Vraiment hack, et probablement pas la meilleure solution, mais bon ça marche.

Fondamentalement, il parcourt le tableau de manière récursive, accumulant les indicateurs actuels dans un tableau.
Une fois que la valeur référencée est atteinte, il prend la "pile" d'index et l'inverse, la plaçant dans le tableau $ out. (Y a-t-il un moyen d'éviter l'utilisation du tableau $ temp?)

2
v3.

Voici une variante de la solution Codler/Andreas qui fonctionne avec les tableaux associatifs. Un peu plus long mais sans boucle:

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

Exemple:

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

J'ai été confronté au même problème. Voici ce que je suis venu avec: 

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

J'avais besoin d'une fonction de transposition prenant en charge le tableau associatif:

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

Et le chemin du retour:

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

L'astuce array_unshift n'a pas fonctionné NOR le array_map...

J'ai donc codé une fonction array_map_join_array pour gérer l'association des clés d'enregistrement:

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

et array_transpose est devenu évident:

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

Utiliser comme ça

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

?>

Résultat:

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

Essayez comme ça

0
MrCharif

Si vous essayez de décompresser les exemples de données du PO avec l'opérateur Splat (...), vous allez générer:

Erreur fatale: Erreur non capturée: impossible de décompresser le tableau avec des clés de chaîne

Preuve

Pour surmonter cette erreur, appelez array_values() pour indexer les clés de premier niveau avant de décompresser.

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

Sortie:

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

Une caractéristique/surprise supplémentaire concernant la transposition avec cette technique est que des éléments null seront générés lorsque les sous-tableaux auront des tailles différentes ... mais peut-être pas où vous pourriez vous attendre.

À partir d'un exemple de données comme ceci

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

La sortie est:

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

Notez le niveau de soin affiché par la fonction (comparable aux bagagistes qui sortent vos bagages du ventre de l’avion). Les identifiants des valeurs initiales des sous-tableaux ne sont pas pris en compte (et peu importe si 1, 2 et 3 étaient x, y, & z); tout ce qui sort de la bande transporteuse est jeté dans le logement disponible le plus bas.

Ce comportement est cohérent et fiable dans la livraison d'une matrice complète. Une variante de boucle foreach() ne livrera pas en natif l'élément null à partir de sous-matrices de tailles différentes et, dans la plupart des implémentations, sa capacité à accéder à toutes les valeurs de sous-matrice dépend de la longueur du premier sous-tableau.

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

Sortie:

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

Comme indiqué ci-dessus, si vous voulez être sûr d'avoir extrait TOUTES les données du tableau en entrée, vous devez écrire une logique d'addition pour fournir tous les identifiants de colonne uniques à la boucle foreach.


p.s. Avant d’apprendre cette syntaxe de transposition abrégée, j’ai écrit un transposeur fonctionnel plus laid, plus bavard qui a fait l’objet de critiques .

0
mickmackusa

Voici une méthode array_walk pour y parvenir,

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

Démo .

0
quickSwap

C’est une autre façon de faire exactement la même chose que la réponse de @codler. Je devais vider quelques tableaux en csv, alors j’utilisais la fonction suivante:

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

Référence de test et de sortie: https://tutes.in/how-to-transpose-an-array-in-php-with-irregular-subarray-size/

0
th3pirat3