program story

PHP에서 다차원 배열 전치

inputbox 2020. 11. 9. 08:07
반응형

PHP에서 다차원 배열 전치


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()하시겠습니까?

편집 : 이것은 숙제가 아닙니다. 나는 SOers가 가장 명백한 경로보다 더 창의적인 솔루션을 가지고 있는지 확인하고 싶습니다. 그러나 몇몇 사람들은보다 일반적인 솔루션에 대해이 문제가 너무 쉽게 어떤 것에 대해 불평 때문에 그는 n과 작품 차원 배열?

즉, 다음과 같이 함수를 어떻게 작성 하시겠습니까?

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

? (ps. for loops이 경우 12 중첩 이 최선의 해결책 이라고 생각하지 않습니다 .)


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 개의 루프로.

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

나는 당신이 배열 전치 (열이 행이되고 행이 열이 됨)를 언급하고 있다고 생각합니다 .

다음은이를위한 기능입니다 (출처) .

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

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

정말 hackish하고 아마도 최상의 솔루션은 아니지만 헤이 작동합니다.

기본적으로 배열에 현재 인덱스를 누적하여 배열을 재귀 적으로 탐색합니다.
참조 된 값에 도달하면 인덱스의 "스택"을 가져 와서 $ out 배열에 넣습니다. ($ temp 배열 사용을 피하는 방법이 있습니까?)


나는 같은 문제에 직면했다. 내가 생각해 낸 것은 다음과 같습니다.

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

연관 배열을 지원하는 전치 함수가 필요했습니다.

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

다음 은 연관 배열과 함께 작동하는 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
    )
);

이렇게 사용

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

?>

결과:

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
        )

)

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

이렇게 시도


시작하기 전에 2 차원 연관 (또는 비 연관) 배열을 변환하는 일반화 된 솔루션을 게시 한 @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);

원하는 전치 행렬을 얻을 수 있습니다.


이것은 @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/


이것을 달성하는 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

데모 .


표시 연산자 ( ...) 를 사용하여 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은 하위 배열의 크기가 다를 때 요소가 생성되지만 예상 할 수있는 위치가 아닐 수도 있다는 것입니다.

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.

참고URL : https://stackoverflow.com/questions/797251/transposing-multidimensional-arrays-in-php

반응형