【问题标题】:How to transpose and reduce a 3-level multidimensional array into a 2-level array? [duplicate]如何将 3 级多维数组转置和归约为 2 级数组? [复制]
【发布时间】:2016-08-28 10:02:00
【问题描述】:

我有一系列这样的产品:

$rows = [
    100 => [
        ['product_id' => 101, 'name' => ''],
        ['product_id' => 102, 'name' => ''],
        ['product_id' => 103, 'name' => ''],
    ],
    200 => [
        ['product_id' => 201, 'name' => ''],
        ['product_id' => 202, 'name' => ''],
    ],
    300 => [
        ['product_id' => 301, 'name' => ''],
        ['product_id' => 302, 'name' => ''],
        ['product_id' => 303, 'name' => ''],
        ['product_id' => 304, 'name' => ''],
    ]
];

我想把它转换成这样的结构,去除一层深度:

$rows = [
    ['product_id' => 101, 'name' => ''],  //1st from 100 subArray
    ['product_id' => 201, 'name' => ''],  //1st from 200 subArray
    ['product_id' => 301, 'name' => ''],  //1st from 300 subArray
    ['product_id' => 102, 'name' => ''],  //2nd from 100 subArray
    ['product_id' => 202, 'name' => ''],  //etc...
    ['product_id' => 302, 'name' => ''],
    ['product_id' => 103, 'name' => ''],
    ['product_id' => 303, 'name' => ''],
    ['product_id' => 304, 'name' => ''],
];

现在我正在尝试使用此代码:

$max_store_products = max(array_map('count', $rows));
$sortedArray = array();

for($j = 0; $j < $max_store_products; $j++)
{
    foreach($rows as $store_id => $store_products)
    {
        $sp = $rows[$store_id][$j];  

        if(isset($sp))
        {
            $sortedArray[] = $rows[$store_id][$j]; 
        }  
        else
            unset($rows[$store_id]);

    }
}

但这确实需要很长时间,也没有给我想要的预期输出。

有没有更简单的方法来做到这一点?

【问题讨论】:

    标签: php arrays multidimensional-array transpose


    【解决方案1】:

    说明

    我们用一个简单的数组来说明:

    Array(
        0 => Array(1, 4, 7),
        1 => Array(2, 5, 8),
        2 => Array(3, 6, 9),
    )
    

    首先我们将array_shift() 一个匿名函数添加到您的数组中,因此您的数组如下所示:

    Array(
        0 => function(){...}
    
        //Your values
        1 => Array(1, 4, 7),
        2 => Array(2, 5, 8),
        3 => Array(3, 6, 9),
    )
    

    然后我们调用call_user_func_array() 并应用array_map() 作为回调。这要做的基本上是:

    //↓ 'array_map()' callback from 'call_user_func_array()'
    array_map(function(){
        //Callback from the First array element
    
    //        Second          Third           Fourth
    //     array element   array element   array element   more elements...
    //          ↓               ↓               ↓           ↓
    }, Array(1, 4, 7), Array(2, 5, 8), Array(3, 6, 9) /* , ... */);
    

    Array(1, 4, 7), Array(2, 5, 8), Array(3, 6, 9) /* , ... */
          │  │  │         │  │  │         │  │  └─ 3 iteration in 'array_map()' | 3 value
          │  │  │         │  │  │         │  └──── 2 iteration in 'array_map()' | 3 value
          │  │  │         │  │  │         └─────── 1 iteration in 'array_map()' | 3 value
          │  │  │         │  │  │
          │  │  │         │  │  └───────────────── 3 iteration in 'array_map()' | 2 value
          │  │  │         │  └──────────────────── 2 iteration in 'array_map()' | 2 value
          │  │  │         └─────────────────────── 1 iteration in 'array_map()' | 2 value
          │  │  │
          │  │  └───────────────────────────────── 3 iteration in 'array_map()' | 1 value
          │  └──────────────────────────────────── 2 iteration in 'array_map()' | 1 value
          └─────────────────────────────────────── 1 iteration in 'array_map()' | 1 value
    

    现在这意味着我们将一次循环遍历所有子数组,逐个值,并将该匿名函数应用于我们循环遍历的所有值。在匿名函数本身中,我们基本上只是 array_merge() 我们在当前迭代中获得的值到结果数组:

    $result = array_merge($result, Array(1, 2, 3));  //First values from each subArray
    $result = array_merge($result, Array(4, 5, 6));  //Second values from each subArray
    $result = array_merge($result, Array(7, 8, 9));  //Third values from each subArray
    //...
    

    代码

    <?php
    
        $arr = []; //Your array here
        $result = [];
        array_unshift($arr, function(...$values)use(&$result){
            $result = array_merge($result, array_filter($values));
        });
    
        call_user_func_array("array_map", $arr);
    
        print_r($result);
    
    ?>
    

    【讨论】:

      【解决方案2】:

      使用,

      function array_flatten($array) { 
       if (!is_array($array)) { 
        return FALSE; 
       } 
       $result = array(); 
       foreach ($array as $key => $value) { 
        if (is_array($value)) { 
         $result = array_merge($result, array_flatten($value)); 
        } 
        else { 
         $result[$key] = $value; 
        } 
       } 
       return $result; 
      } 
      

      【讨论】:

        【解决方案3】:

        单线

        此解决方案不依赖于数组值的唯一性和可排序性。它将通过始终从每个数组中取出一个元素来合并所有数组。

        $result = array_filter(array_reduce(array_map(null, ...$rows), 'array_merge' ,[]));
        
        var_dump($rows);
        

        说明

        让我们分解一下:

        $columns = array_map(null, ...$rows);
        

        当您将null 作为array_map 的第一个参数传递时,它的行为与:

        $columns = array_map(function (...$vals) {
            return $vals;
        }, ...$rows);
        

        这基本上转置了$rows,因此您会得到一个数组数组,其中包含每个部分的第一个、第二个、第三个等元素。传递null 而不是回调可能看起来很奇怪,但它实际上并不是docs 中描述的黑客攻击。

        $rawResult = array_reduce($columns, 'array_merge', []);
        

        在这里您只需将所有列合并到一个数组中。

        $result = array_filter($rawResult);
        

        array_filter 删除任何 NULL 条目,以防您的内部数组长度不均。

        旧方案:先合并,后排序

        假设您的值需要排序,例如您的示例中的product_id,您可以先合并数组,然后对结果进行排序:

        $result = array_merge(...$rows);
        
        usort($result, function ($a, $b) {
            return $a['product_id'] % 100 - $b['product_id'] % 100 ?: $a['product_id'] - $b['product_id'];
        });
        
        var_dump($result); // merged & sorted result
        

        【讨论】:

        • 我认为你没有完全理解我的意思。 $result = array_merge(...$rows);很有用,我非常接近解决问题..
        • 好吧,那我错过了什么?我以为您只想合并数组并以特定方式对结果进行排序。
        • 我实际上想出了一个很酷的单衬里,它不依赖于对物品进行分类的能力。以防你觉得有趣。
        • @KubaBirecki 有趣的单线解决方案,有很好的解释!同样从您在答案中使用的新答案风格和词汇来看,我猜您也阅读了我的答案;)
        • @Rizier123 这不是我读到的你的第一个答案 :) 其中一些很有启发性。我也喜欢你的解决方案,这就是让我尝试进一步推动它的原因。
        【解决方案4】:

        很确定这是您想要的:

        <?php
        
        $result = [];
        // Get the highest number of items in an entry of `$rows`
        $maxItems = 0;
        foreach($rows as $row) {
           if (count($row)>$maxItems) $maxItems = $row;
        }
        
        for($ii=0; $ii < $maxItems; $ii++) {
        
           foreach($rows as $row) {
        
             if (isset($row[$ii])) {
               $result[] = $row[ii];
             }
        
           }
        
        }
        
        ?>
        

        您自己的版本失败的原因是因为您有这行:

        if(isset($sp))
        

        但是,$sp 总是在那里定义。 PHP 没有像 javascript 那样的 undefined 类型,所以一旦你从数组中获取一个项目(无论它是否存在),它就会被定义,抛出一个 E_NOTICE 并导致 null。你真的应该在 PHP 中增加错误消息,因为这很明显。

        现在,至于速度,您可以做很多事情来改进它。你可以:

        1. 删除第一个循环以获取计数并将其与第二个循环结合起来,但我怀疑这会产生很大的不同,除非循环实际上很大。
        2. 如果内存是您的主要瓶颈,您可以尝试切换到使用生成器的方法。纯粹从 CPU 的角度来看,这会慢一些,但如果您的源数据绝对庞大,并且您的 PHP 脚本导致您的服务器进行交换以应对,那么基于流的、基于生成器的方法可能会为您提供更好的性能。李>

        无论如何,如果您首先考虑一下为什么要这样做,并且如果有更明智的方法来解决这个问题而不只是修复您的循环,这可能会更好。如果你真的因为处理大量数据而导致性能不佳,那么可能需要修复的是应用程序设计,而不是这个循环。

        【讨论】:

          猜你喜欢
          • 2020-09-12
          • 1970-01-01
          • 1970-01-01
          • 2014-11-02
          • 1970-01-01
          • 2015-06-09
          • 2021-10-22
          • 2018-02-01
          • 1970-01-01
          相关资源
          最近更新 更多