【问题标题】:Converting flat array into an array grouped by categories将平面数组转换为按类别分组的数组
【发布时间】:2017-09-29 13:51:08
【问题描述】:

我有一个如下所示的数据库表:

uid | group  | category
1   | group1 | cat1
2   | group1 | cat2
3   | group2 | cat3
4   | group2 | cat4
5   | group2 | cat5
6   | group3 | cat6
7   | group3 | cat7

但我需要将这些数据放在一个数组中,该数组按 group 对类别进行分组。

例如,我的数组应该是这样的:

Array
(
    [group1] => Array
        (
            [0] => Array
                (
                    [0] => 1
                    [1] => cat1

                )

            [1] => Array
                (
                    [0] => 2
                    [1] => cat2
                )

        )

    [group2] => Array
        (
            [0] => Array
                (
                    [0] => 3
                    [1] => cat3
                )

            [1] => Array
                (
                    [0] => 4
                    [1] => cat4
                )

            [2] => Array
                (
                    [0] => 5
                    [1] => cat5
                )

        )

    [group3] => Array
        (
            [0] => Array
                (
                    [0] => 6
                    [1] => cat6
                )
            [1] => Array
                (
                    [0] => 7
                    [1] => cat7
                )

        )

)

我已经写了一个 foreach 循环来做这个,但是我有一个问题。

我的问题是它总是遗漏表格的最后一行,我不知道如何解决它。在我看来,逻辑表明它应该始终有效。

我在想,在循环之后我可以将最后一行添加到新数组中,但我认为如果最后一行有不同的组,这可能会导致问题,我宁愿将解决方案内置到 foreach循环。

不幸的是,我在这里不知所措。如何修复我的代码以包含数据库查询的最后一行?

我也很想看看我可以对我当前的代码进行哪些改进,但这对于 codereview 来说可能是一个更好的问题。

我的循环:

$pass = [];
foreach($stmt as $key => $value) {
    if(empty($currentGroup)) $currentGroup = $value['group'];
    if(empty($temp)) $temp = [];
    if($currentGroup != $value['group'] || $key+1 == count($stmt)) {
        $pass[$currentGroup] = $temp;
        $currentGroup = $value['group'];
        $temp = [];
        $temp[] = [$stmt[$key]['uid'], $stmt[$key]['category']];
    } else {
        $temp[] = [$stmt[$key]['uid'], $stmt[$key]['category']];
    }
}

【问题讨论】:

    标签: php arrays


    【解决方案1】:

    我最近又需要这个,所以我根据@JParkinson1991的回答做了一个函数。

    我把它放在这里是为了记录,并可能帮助未来的读者。

    function groupArray($arr, $group, $preserveSubArrays = false, $preserveGroupKey = false) {
        $temp = array();
        foreach($arr as $key => $value) {
            $groupValue = $value[$group];
            if(!$preserveGroupKey)
            {
                unset($arr[$key][$group]);
            }
            if(!array_key_exists($groupValue, $temp)) {
                $temp[$groupValue] = array();
            }
    
            if(!$preserveSubArrays){
                $data = count($arr[$key]) == 1? array_pop($arr[$key]) : $arr[$key];
            } else {
                $data = $arr[$key];
            }
            $temp[$groupValue][] = $data;
        }
        return $temp;
    }
    

    故障

    function groupArray($arr, $group, $preserveGroupKey = false, $preserveSubArrays = false)
    

    此函数接受 2 到 4 个参数。

    1. 要分组的平面数组(数组)
    2. 要分组的键(字符串/整数)
    3. 在每个子数组的输出中保留组键的选项(布尔值)
    4. 保留子数组的选项。如果每个子数组中仅存在 1 个键,则该函数将仅存储每行的单个值,而不是数组(布尔值)

    第一个参数是数组本身,第二个参数是要分组的键,第三个(可选)参数是一个布尔值,告诉函数是否要在子数组中保留组键.


    $temp = array();
    foreach($arr as $key => $value) {
        $groupValue = $value[$group];
        if(!$preserveGroupKey)
        {
            unset($arr[$key][$group]);
        }
        if(!array_key_exists($groupValue, $temp)) {
            $temp[$groupValue] = array();
        }
        $temp[$groupValue][] = $arr[$key];
    }
    

    首先,我们创建一个名为$temp的临时数组

    接下来,我们遍历数组,获取键(应该是字符串或 int)和值(应该是数组)。

    我们将$groupValue 设置为您选择的$group 的任何值,例如下面示例中的“组”。

    $arr = [
        0 => [
            "group" => "group1",
            "name" => "Bob",
        ],
        1 => [
            "group" => "group1",
            "name" => "Randy",
        ],
        2 => [
            "group" => "group1",
            "name" => "Susan",
        ],
        3 => [
            "group" => "group2",
            "name" => "Larry",
        ],
        4 => [
            "group" => "group2",
            "name" => "David",
        ],
        5 => [
            "group" => "group3",
            "name" => "Perry",
        ],
    ];
    

    然后我们检查是否要$preserveGroupKey's。如果这个布尔值是假的(默认情况下),键将被删除,留下几个子数组,只剩下“名称”键。

    现在我们检查$groupValue 是否存在于我们的$temp 数组中,如果不存在,我们就创建它。

    然后我们将当前行的值添加到$temp[$groupValue]。从上面的例子中,我们最终会得到:

    Array
    (
        [group1] => Array
            (
                [0] => Bob
                [1] => Randy
                [2] => Susan
            )
    
        [group2] => Array
            (
                [0] => Larry
                [1] => David
            )
    
        [group3] => Array
            (
                [0] => Perry
            )
    
    )
    

    或者,将第三个参数设置为 true,您将得到:

    Array
    (
        [group1] => Array
            (
                [0] => Array
                    (
                        [name] => Bob
                    )
    
                [1] => Array
                    (
                        [name] => Randy
                    )
    
                [2] => Array
                    (
                        [name] => Susan
                    )
    
            )
    
        [group2] => Array
            (
                [0] => Array
                    (
                        [name] => Larry
                    )
    
                [1] => Array
                    (
                        [name] => David
                    )
    
            )
    
        [group3] => Array
            (
                [0] => Array
                    (
                        [name] => Perry
                    )
    
            )
    
    )
    

    【讨论】:

      【解决方案2】:

      以下应该这样做:

      <?php
      
      //Create an array to store our grouped rows
      $grouped = array();
      
      //Loop over all rows returned by the $stmt that has been executed.
      //You could probably remove the key from here, it's not needed it seems.
      //The keys within the $value array will match the names of the columns in 
      //the database,
      foreach($stmt as $key => $value){
      
          //As we're storing by the group value from the row we first want to
          //check if our grouped array contains a key for the group of the row
          //being processed. If it does not, create an empty array within the
          //grouped data for this group.
          if(!array_key_exists($value['group'], $grouped)){
              $grouped[$value['group']] = array();
          }
      
          //Knowing we will always have an array element for the rows group
          //we can blindly append the values for this row to the grouped 
          //container using its values.
          //'[] =' is just short hand append.
          $grouped[$value['group']][] = array(
              $value['uid'],
              $value['category']
          );
      }
      

      希望有帮助!


      为了进一步证明这个循环,您可以将分组值追加更改为以下内容:

      <?php
      
      //Setting the whole row (minus the group) rather than just the uid 
      //and category explicitly allows this code to work without modification
      //as the datatable changes, ie. new columns. Assuming that is the 'group'
      //column remains present
      unset($value['group']);
      $grouped[$value['group']][] = $value;
      

      现在可以使用以下方式访问分组内容数据:

      <?php
      
      //Acceess data via column name not array index, yay!
      echo $grouped['group1']['uid']
      

      【讨论】:

      • 感谢代码 sn-p,它可能会给我一些即时帮助,但我更愿意了解我做错了什么,以及如何修复我当前的代码,或者至少要通过解释了解你的 sn-p 是如何工作的。我可以理解您的代码,但未来的读者可能无法理解。
      • 没问题,我已经在代码周围添加了更多的 cmets,如果您需要任何其他指针,请阅读并告诉我。
      • 这里有语法错误if(!array_key_exists($value['group'], $grouped){,你缺少和结束括号。此外,我能够将其从 9 行代码压缩为 4 行代码 pastebin.com/jMbEF9PV 供任何未来的读者使用
      • 修复了语法错误。对于您的压缩(我不会将其归类为压缩,代码相同,只是格式不同),我建议您尽可能保持代码可读性。当你在几个月后回到它时,再次拿起它会更容易。如果您担心用于生产的缩小代码,这总是可以完成的。对我来说,虽然我总是会推荐一个评论丰富、可读性强的开发源代码。
      • 我在很大程度上同意代码的可读性,但对于这种特殊情况,它实际上只是在为下拉框加油,我不希望它会改变。我也是项目中唯一的 PHP 开发人员,我可以很好地阅读这段代码 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-30
      • 2020-03-22
      • 2020-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多