【问题标题】:How to group subarrays by a column value?如何按列值对子数组进行分组?
【发布时间】:2012-09-24 06:07:19
【问题描述】:

我有以下数组

Array
(
    [0] => Array
        (
            [id] => 96
            [shipping_no] => 212755-1
            [part_no] => reterty
            [description] => tyrfyt
            [packaging_type] => PC
        )

    [1] => Array
        (
            [id] => 96
            [shipping_no] => 212755-1
            [part_no] => dftgtryh
            [description] => dfhgfyh
            [packaging_type] => PC
        )

    [2] => Array
        (
            [id] => 97
            [shipping_no] => 212755-2
            [part_no] => ZeoDark
            [description] => s%c%s%c%s
            [packaging_type] => PC
        )

)

如何按id 对数组进行分组?是否有任何本机 php 函数可用于执行此操作?

虽然这种方法有效,但我想使用foreach 来执行此操作,因为使用上述方法我会得到重复的项目,这是我试图避免的?

在上面的例子中id有2个项目,所以它需要在id里面

【问题讨论】:

  • 你也想删除重复的???
  • 大多数解决方案使用一个 FOREACH。
  • @JustinJohn 大多数解决方案都使用 ONE FOREACH 进行数组创建,最终结果不是数组。我正在寻找更好的解决方案。
  • 你的意思是最终结果不是一维数组。
  • 我的意思是..我需要foreach创建的数组将其转换为html元素的值。

标签: php arrays grouping


【解决方案1】:

没有原生的,用循环就好了。

$result = array();
foreach ($data as $element) {
    $result[$element['id']][] = $element;
}

【讨论】:

  • 你不需要if——真正的部分在这两种情况下都有效。
  • 这里不需要 if 条件。这与$result[$id][] = $data 相同。试试看。
  • 这怎么知道相似的id?
  • 一段很棒的代码,也有类似的问题。但最重要的是,我需要获取重复 id 的数量
  • 你最后忘了ksort($result, SORT_NUMERIC);。问题显示排序键
【解决方案2】:

您可以尝试以下方法:

$group = array();

foreach ( $array as $value ) {
    $group[$value['id']][] = $value;
}

var_dump($group);

输出:

array
  96 => 
    array
      0 => 
        array
          'id' => int 96
          'shipping_no' => string '212755-1' (length=8)
          'part_no' => string 'reterty' (length=7)
          'description' => string 'tyrfyt' (length=6)
          'packaging_type' => string 'PC' (length=2)
      1 => 
        array
          'id' => int 96
          'shipping_no' => string '212755-1' (length=8)
          'part_no' => string 'dftgtryh' (length=8)
          'description' => string 'dfhgfyh' (length=7)
          'packaging_type' => string 'PC' (length=2)
  97 => 
    array
      0 => 
        array
          'id' => int 97
          'shipping_no' => string '212755-2' (length=8)
          'part_no' => string 'ZeoDark' (length=7)
          'description' => string 's%c%s%c%s' (length=9)
          'packaging_type' => string 'PC' (length=2)

【讨论】:

    【解决方案3】:

    在更函数化的编程风格中,您可以使用array_reduce

    $groupedById = array_reduce($data, function (array $accumulator, array $element) {
      $accumulator[$element['id']][] = $element;
    
      return $accumulator;
    }, []);
    

    【讨论】:

      【解决方案4】:

      受 .NET LINQ 的启发,我只是把它放在一起

      <?php
      
      // callable type hint may be "closure" type hint instead, depending on php version
      function array_group_by(array $arr, callable $key_selector) {
        $result = array();
        foreach ($arr as $i) {
          $key = call_user_func($key_selector, $i);
          $result[$key][] = $i;
        }  
        return $result;
      }
      
       $data = array(
              array(1, "Andy", "PHP"),
              array(1, "Andy", "C#"),
              array(2, "Josh", "C#"),
              array(2, "Josh", "ASP"),
              array(1, "Andy", "SQL"),
              array(3, "Steve", "SQL"),
          );
      
      $grouped = array_group_by($data, function($i){  return $i[0]; });
      
      var_dump($grouped);
      
      ?>
      

      瞧,你得到了

      array(3) {
        [1]=>
        array(3) {
          [0]=>
          array(3) {
            [0]=>
            int(1)
            [1]=>
            string(4) "Andy"
            [2]=>
            string(3) "PHP"
          }
          [1]=>
          array(3) {
            [0]=>
            int(1)
            [1]=>
            string(4) "Andy"
            [2]=>
            string(2) "C#"
          }
          [2]=>
          array(3) {
            [0]=>
            int(1)
            [1]=>
            string(4) "Andy"
            [2]=>
            string(3) "SQL"
          }
        }
        [2]=>
        array(2) {
          [0]=>
          array(3) {
            [0]=>
            int(2)
            [1]=>
            string(4) "Josh"
            [2]=>
            string(2) "C#"
          }
          [1]=>
          array(3) {
            [0]=>
            int(2)
            [1]=>
            string(4) "Josh"
            [2]=>
            string(3) "ASP"
          }
        }
        [3]=>
        array(1) {
          [0]=>
          array(3) {
            [0]=>
            int(3)
            [1]=>
            string(5) "Steve"
            [2]=>
            string(3) "SQL"
          }
        }
      }
      

      【讨论】:

        【解决方案5】:

        使用并缓存您要分组的列值,然后将剩余数据作为您在结果中创建的组的新子数组推送。

        function array_group(array $data, $by_column)
        {
            $result = [];
            foreach ($data as $item) {
                $column = $item[$by_column];
                unset($item[$by_column]);
                $result[$column][] = $item;
            }
            return $result;
        }
        

        【讨论】:

          【解决方案6】:

          如果您需要具有全套测试的 Composer 替代方案,array_group_by 函数可以满足您的需求。全面披露:我是该库的作者。

          $grouped = array_group_by($arr, 'id');
          

          它还支持多级分组,甚至通过使用自定义回调函数进行复杂分组:

          // Multilevel grouping
          $grouped = array_group_by($arr, 'id', 'part_no');
          
          // Grouping by a callback/callable function
          $grouped = array_group_by($records, function ($row) {
              return $row->city;
          });
          

          【讨论】:

          • 警告:此方法array_group_by 引入变量$key 作为方法参数并在foreach 中被覆盖
          • 如果那个 github 链接每次都死掉或移动,这个答案将变得毫无用处。您的工作解决方案应该完全/静态地写在您的答案中。如果您是所引用代码的所有者,则应明确注明出处。请edit你的答案。
          • 在您的自定义函数中,$groupKey = null; 无用,因为它被无条件覆盖。 elseif 在 PHP 中应该是一个词。 func_num_args() 真的需要多次调用吗?
          • @mickmackusa 我已经更新了答案以反映链接库的所有权。这是我承认的错误。对于您的其他观点,我不同意一个完整的书面示例是必要的。虽然可能,但将 50 行代码复制到 Stack Overflow 中是不切实际的,因为整个目的是提供一个库/Composer 替代未经测试和有限的代码。 Composer 是 PHP 的关键时刻,如果没有它,PHP 将成为足够大的应用程序维护的噩梦。
          • @mickmackusa 根据$groupKey 的评论,它并非没用。即使 PHP 没有必要,也最好初始化变量。 PHP 自己的手册说明了这一点。此外,在许多语言中,在嵌套语句中创建的变量在嵌套之外不可用。是的,func_num_args 在技术上不需要调用,如果长度是通过递归调用传递并在每个嵌套中减去,但这种微优化会导致代码可读性降低,收益很少。
          【解决方案7】:

          $arr = 数据排列;

          $fldName = 按列名分组;

          function array_group_by( $arr, $fldName) {
              $groups = array();
              foreach ($arr as $rec) {
                  $groups[$rec[$fldName]] = $rec;
              }
              return $groups;
          }
          
          function object_group_by( $obj, $fldName) {
              $groups = array();
              foreach ($obj as $rec) {
                  $groups[$rec->$fldName] = $rec;
              }
              return $groups;
          }
          

          【讨论】:

            【解决方案8】:
            $arr = array();
            
            foreach($old_arr as $key => $item)
            {
               $arr[$item['id']][$key] = $item;
            }
            
            ksort($arr, SORT_NUMERIC);
            

            【讨论】:

            • 拜托,__首先我需要操作数组?然后再foreach真正的目的?
            【解决方案9】:
            for($i = 0 ; $i < count($arr)  ; $i++ )
            {
                $tmpArr[$arr[$i]['id']] = $arr[$i]['id'];
            }
            $vmpArr = array_keys($tmpArr);
            print_r($vmpArr);
            

            【讨论】:

            • 这个答案是非常错误的——它只是返回键并且会杀死重复项。它还使用了在每次迭代时调用count() 的不良做法。仅代码的答案在 SO 上的价值很低。我可能永远无法理解支持者。
            【解决方案10】:

            扩展@baba 的答案,我喜欢,但创建了一个更复杂的三层深度多维(数组(数组(数组))):

            $group = array();
             foreach ( $array as $value ) {
               $group[$value['id']][] = $value; 
             }
            
            // output only data from id 96
            foreach ($group as $key=>$value) { //outer loop
             foreach ($value as $k=>$v){ //inner loop
              if($key==96){ //if outer loop is equal to 96 (could be variable)
               for ($i=0;$i<count($k);$i++){ //iterate over the inner loop
                    printf($key.' has a part no. of '.$v['part_no'].' and shipping no. of '.$v['shipping_no'].'<br>');
               }
             }
            }
             }
            

            将输出:

            96 有一个零件号。 reterty 和运输编号 212755-1

            96 有一个零件号。 dftgtryh 和运输编号 212755-1

            【讨论】:

              【解决方案11】:

              使用 LINQ 很简单,它在 PHP 中的多个库中实现,包括 YaLinqo*。它允许对数组和对象执行类似 SQL 的查询。 groubBy 函数是专门为分组而设计的,您只需要指定要分组的字段:

              $grouped_array = from($array)->groupBy('$v["id"]')->toArray();
              

              其中'$v["id"]' 是此库支持的function ($v) { return $v["id"]; } 的简写。

              结果将与接受的答案完全相同,只是代码更少。

              *我开发的

              【讨论】:

              • 你能告诉我如何按多个字段分组吗?在我的用例中,我有一个带有日期字段的对象,需要按年、月和日期对其进行分组。像 { 2016 => { 09 => { 15 => $object } } }
              • @fnagel 嵌套分组有点复杂。请参阅support issue about nested groupBy。它包括一个辅助函数group_by_multiple,以及对嵌套分组如何工作的详细解释。使用此函数,您的查询将类似于group_by_multiple($objects, [ '$v-&gt;date-&gt;format("Y")', '$v-&gt;date-&gt;format("n")', '$v-&gt;date-&gt;format("j")' ])
              【解决方案12】:

              1. GROUP BY一键

              此函数用作数组的GROUP BY,但有一个重要限制:只能使用一个分组“列”($identifier)。

              function arrayUniqueByIdentifier(array $array, string $identifier)
              {
                  $ids = array_column($array, $identifier);
                  $ids = array_unique($ids);
                  $array = array_filter($array,
                      function ($key, $value) use($ids) {
                          return in_array($value, array_keys($ids));
                      }, ARRAY_FILTER_USE_BOTH);
                  return $array;
              }
              

              2。检测表的唯一行(二维数组)

              此功能用于过滤“行”。如果我们说,一个二维数组是一个表,那么它的每个元素就是一行。因此,我们可以使用此功能删除重复的行。如果它们的所有列(第二维的元素)相等,则两行(第一维的元素)相等。适用于“列”值的比较:如果一个值是简单的type,则该值本身将用于比较;否则将使用其类型(arrayobjectresourceunknown type)。

              策略很简单:从原始数组做一个浅数组,其中元素是原始数组的imploded“列”;然后在上面应用array_unique(...);最后使用检测到的 ID 过滤原始数组。

              function arrayUniqueByRow(array $table = [], string $implodeSeparator)
              {
                  $elementStrings = [];
                  foreach ($table as $row) {
                      // To avoid notices like "Array to string conversion".
                      $elementPreparedForImplode = array_map(
                          function ($field) {
                              $valueType = gettype($field);
                              $simpleTypes = ['boolean', 'integer', 'double', 'float', 'string', 'NULL'];
                              $field = in_array($valueType, $simpleTypes) ? $field : $valueType;
                              return $field;
                          }, $row
                      );
                      $elementStrings[] = implode($implodeSeparator, $elementPreparedForImplode);
                  }
                  $elementStringsUnique = array_unique($elementStrings);
                  $table = array_intersect_key($table, $elementStringsUnique);
                  return $table;
              }
              

              如果它的类型是object,也可以改进比较,检测“列”值的类。

              $implodeSeparator 应该或多或少复杂,z.B。 spl_object_hash($this).


              3.检测表中具有唯一标识符列的行(二维数组)

              此解决方案依赖于第二个。现在完整的“行”不需要是唯一的。两个“行”(第一个维度的元素)现在相等,如果一个“行”的所有 相关“字段”(第二个维度的元素)都等于相应的“字段”(具有相同键的元素)。

              “相关”“字段”是“字段”(第二维度的元素),其键等于传递的“标识符”的元素之一。

              function arrayUniqueByMultipleIdentifiers(array $table, array $identifiers, string $implodeSeparator = null)
              {
                  $arrayForMakingUniqueByRow = $removeArrayColumns($table, $identifiers, true);
                  $arrayUniqueByRow = $arrayUniqueByRow($arrayForMakingUniqueByRow, $implodeSeparator);
                  $arrayUniqueByMultipleIdentifiers = array_intersect_key($table, $arrayUniqueByRow);
                  return $arrayUniqueByMultipleIdentifiers;
              }
              
              function removeArrayColumns(array $table, array $columnNames, bool $isWhitelist = false)
              {
                  foreach ($table as $rowKey => $row) {
                      if (is_array($row)) {
                          if ($isWhitelist) {
                              foreach ($row as $fieldName => $fieldValue) {
                                  if (!in_array($fieldName, $columnNames)) {
                                      unset($table[$rowKey][$fieldName]);
                                  }
                              }
                          } else {
                              foreach ($row as $fieldName => $fieldValue) {
                                  if (in_array($fieldName, $columnNames)) {
                                      unset($table[$rowKey][$fieldName]);
                                  }
                              }
                          }
                      }
                  }
                  return $table;
              }
              

              【讨论】:

              • in_array() 的迭代调用不是最佳做法。
              • 检测唯一行与分组有何关系?
              【解决方案13】:

              这应该组合一个关联数组 Ejm 按国家/地区分组

              function getGroupedArray($array, $keyFieldsToGroup) {   
                  $newArray = array();
              
                  foreach ($array as $record) 
                      $newArray = getRecursiveArray($record, $keyFieldsToGroup, $newArray);
              
                  return $newArray;
              }
              function getRecursiveArray($itemArray, $keys, $newArray) {
                  if (count($keys) > 1) 
                      $newArray[$itemArray[$keys[0]]] = getRecursiveArray($itemArray,    array_splice($keys, 1), $newArray[$itemArray[$keys[0]]]);
                  else
                      $newArray[$itemArray[$keys[0]]][] = $itemArray;
              
                  return $newArray;
              }
              
              $countries = array(array('Country'=>'USA', 'State'=>'California'),
                                 array('Country'=>'USA', 'State'=>'Alabama'),
                                 array('Country'=>'BRA', 'State'=>'Sao Paulo'));
              
              $grouped = getGroupedArray($countries, array('Country'));
              

              【讨论】:

                【解决方案14】:

                Nspl检查indexed函数:

                use function \nspl\a\indexed;
                $grouped = indexed($data, 'id');
                

                【讨论】:

                  【解决方案15】:
                  function array_group_by($arr, array $keys) {
                  
                  if (!is_array($arr)) {
                      trigger_error('array_group_by(): The first argument should be an array', E_USER_ERROR);
                  }
                  if (count($keys)==0) {
                      trigger_error('array_group_by(): The Second argument Array can not be empty', E_USER_ERROR);
                  }
                  
                  // Load the new array, splitting by the target key
                  $grouped = [];
                  foreach ($arr as $value) {
                      $grouped[$value[$keys[0]]][] = $value;
                  }
                  
                  // Recursively build a nested grouping if more parameters are supplied
                  // Each grouped array value is grouped according to the next sequential key
                  if (count($keys) > 1) {
                          foreach ($grouped as $key => $value) {
                         $parms = array_merge([$value], [array_slice($keys, 1,count($keys))]);
                         $grouped[$key] = call_user_func_array('array_group_by', $parms);
                  
                      }
                  }
                  return $grouped;
                  

                  }

                  【讨论】:

                    【解决方案16】:
                    function groupeByPHP($array,$indexUnique,$assoGroup,$keepInOne){
                    $retour = array();
                    $id = $array[0][$indexUnique];
                    foreach ($keepInOne as $keep){
                        $retour[$id][$keep] = $array[0][$keep];
                    }
                    foreach ($assoGroup as $cle=>$arrayKey){
                        $arrayGrouped = array();
                            foreach ($array as $data){
                                if($data[$indexUnique] != $id){
                                    $id = $data[$indexUnique];
                                    foreach ($keepInOne as $keep){
                                        $retour[$id][$keep] = $data[$keep];
                                    }
                                }
                                foreach ($arrayKey as $val){
                                    $arrayGrouped[$val] = $data[$val];
                                }
                                $retour[$id][$cle][] = $arrayGrouped;
                                $retour[$id][$cle] = array_unique($retour[$id][$cle],SORT_REGULAR);
                            }
                    }
                    return  $retour;
                    }
                    

                    试试这个功能

                    groupeByPHP($yourArray,'id',array('desc'=>array('part_no','packaging_type')),array('id','shipping_no')) 
                    

                    【讨论】:

                      【解决方案17】:

                      递归函数从第一个到最后一个键对二维数组进行分组

                      输入:

                      $arr = array(
                          '0' => array(
                              'key0' => 'value0',
                              'key1' => 'value1',
                              'key2' => 'value02',
                          ),
                          '2' => array(
                              'key0' => 'value0',
                              'key1' => 'value1',
                              'key2' => 'value12',
                          ),
                          '3' => array(
                              'key0' => 'value0',
                              'key1' => 'value3',
                              'key2' => 'value22',
                          ),
                      );
                      $keys = array('key0', 'key1', 'key2');
                      

                      输出:

                      $arr = array(
                          'value0' => array(
                              'value1 => array(
                                  'value02' => null,
                                  'value12' => null,
                              ),
                              'value3' => 'value22',
                          ),
                      );
                      

                      代码:

                      function array_group_by_keys(&$arr, $keys) {
                      
                          if (count($arr) < 2){
                              $arr = array_shift($arr[0]);
                              return;
                          }
                      
                          foreach ($arr as $k => $item) {
                              $fvalue = array_shift($item);
                              $arr[$fvalue][] = $item;
                              unset($arr[$k]);
                          }
                      
                          array_shift($keys);
                          foreach ($arr as &$sub_arr) {
                              array_group_by_keys($sub_arr, $keys);
                          }
                      }
                      

                      【讨论】:

                        【解决方案18】:

                        多级分组怎么样。

                        数据:

                        $rows = [
                            ['country'=>'Japan',  'city'=>'Tokyo', 'surname'=>'Miyazaki', 'name'=>'Hayao'],
                            ['country'=>'France', 'city'=>'Paris', 'surname'=>'Godard',   'name'=>'Jean-Luc'],
                            ['country'=>'France', 'city'=>'Lyon',  'surname'=>'Godard',   'name'=>'Marguerite'],
                            ['country'=>'Japan',  'city'=>'Tokyo', 'surname'=>'Miyazaki', 'name'=>'Akira'],
                            ['country'=>'Japan',  'city'=>'Nara',  'surname'=>'Kurosawa', 'name'=>'Akira'],
                            ['country'=>'France', 'city'=>'Paris', 'surname'=>'Duras',    'name'=>'Marguerite'],
                        ];
                        $groups = groupBy($rows, 'country', 'city', 'surname');
                        

                        代码:

                            function groupBy($rows, ...$keys)
                            {
                                if ($key = array_shift($keys)) {
                                    $groups = array_reduce($rows, function ($groups, $row) use ($key) {
                                        $group = is_object($row) ? $row->{$key} : $row[$key]; // object is available too.
                                        $groups[$group][] = $row;
                                        return $groups;
                                    }, []);
                                    if ($keys) {
                                        foreach ($groups as $subKey=>$subRows) {
                                            $groups[$subKey] = self::groupBy($subRows, ...$keys);
                                        }
                                    }
                                }
                                return $groups;
                            }
                        

                        【讨论】:

                          猜你喜欢
                          • 2011-11-26
                          • 1970-01-01
                          • 2017-07-05
                          • 1970-01-01
                          • 2018-05-01
                          • 2016-10-10
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多