How to group subarrays by a column value?

PhpArraysGrouping

Php Problem Overview


I have the following array

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
        )

)

How can I group the array by id? Is there any native php functions are available to do this?

While this approach works, I want to do this using a foreach, since with the above I will get duplicate items, which I'm trying to avoid?

On the above example id have 2 items, so its need to be inside of the id

Php Solutions


Solution 1 - Php

There is no native one, just use a loop.

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

Solution 2 - Php

You can try the following:

$group = array();

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

var_dump($group);

Output:

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)

Solution 3 - Php

In a more functional programming style, you could use array_reduce

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

  return $accumulator;
}, []);

Solution 4 - Php

I just threw this together, inspired by .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);

?>

And voila you get

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

Solution 5 - Php

Consume and cache the column value that you want to group by, then push the remaining data as a new subarray of the group you have created in the the result.

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

Solution 6 - Php

If you desire a Composer alternative with a full suite of tests, the array_group_by function achieves what you are looking for. Full disclosure: I am the author of said library.

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

It also supports multi-level groupings, or even complex grouping through use of custom callback functions:

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

Solution 7 - Php

$arr = Data Araay;

$fldName = Group By Colum Name;

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

Solution 8 - Php

$arr = array();

foreach($old_arr as $key => $item)
{
   $arr[$item['id']][$key] = $item;
}

ksort($arr, SORT_NUMERIC);

Solution 9 - Php

for($i = 0 ; $i < count($arr)  ; $i++ )
{
    $tmpArr[$arr[$i]['id']] = $arr[$i]['id'];
}
$vmpArr = array_keys($tmpArr);
print_r($vmpArr);

Solution 10 - Php

Expanding on @baba's answer, which I like, but creates a more complex three level deep multi-dimensional (array(array(array))):

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

Will output:

96 has a part no. of reterty and shipping number of 212755-1

96 has a part no. of dftgtryh and shipping number of 212755-1

Solution 11 - Php

It's trivial to do with LINQ, which is implemented in PHP in several libraries, including YaLinqo*. It allows performing SQL-like queries on arrays and objects. The groubBy function is designed specifically for grouping, you just need to specify the field you want to group by:

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

Where '$v["id"]' is a shorthand for function ($v) { return $v["id"]; } which this library supports.

The result will be exactly like in the accepted answer, just with less code.

* developed by me

Solution 12 - Php

1. GROUP BY one key

This function works as GROUP BY for array, but with one important limitation: Only one grouping "column" ($identifier) is possible.

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. Detecting the unique rows for a table (twodimensional array)

This function is for filtering "rows". If we say, a twodimensional array is a table, then its each element is a row. So, we can remove the duplicated rows with this function. Two rows (elements of the first dimension) are equal, if all their columns (elements of the second dimension) are equal. To the comparsion of "column" values applies: If a value is of a simple type, the value itself will be use on comparing; otherwise its type (array, object, resource, unknown type) will be used.

The strategy is simple: Make from the original array a shallow array, where the elements are imploded "columns" of the original array; then apply array_unique(...) on it; and as last use the detected IDs for filtering of the original array.

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

It's also possible to improve the comparing, detecting the "column" value's class, if its type is object.

The $implodeSeparator should be more or less complex, z.B. spl_object_hash($this).


3. Detecting the rows with unique identifier columns for a table (twodimensional array)

This solution relies on the 2nd one. Now the complete "row" doesn't need to be unique. Two "rows" (elements of the first dimension) are equal now, if all relevant "fields" (elements of the second dimension) of the one "row" are equal to the according "fields" (elements with the same key).

The "relevant" "fields" are the "fields" (elements of the second dimension), which have key, that equals to one of the elements of the passed "identifiers".

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

Solution 13 - Php

This should group an associative array Ejm Group By Country

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

Solution 14 - Php

Check indexed function from Nspl:

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

Solution 15 - Php

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;

}

Solution 16 - Php

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

Try this function

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

Solution 17 - Php

> Recursive function grouping 2-dimensional array by keys from first to last > > Input:

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

> Output:

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

> Code:

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

Solution 18 - Php

How about multiple level grouping.

data:

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

code:

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

Solution 19 - Php

It's easy, you can group by any "key" in the array by using my function groupBy();

$data = [
    [
        "id" => 96,
        "shipping_no" => "212755-1",
        "part_no" => "reterty",
        "description" => "tyrfyt",
        "packaging_type" => "PC"
    ],
    [
        "id" => 96,
        "shipping_no" => "212755-1",
        "part_no" => "dftgtryh",
        "description" => "dfhgfyh",
        "packaging_type" => "PC"
    ],
    [
        "id" => 97,
        "shipping_no" => "212755-2",
        "part_no" => "ZeoDark",
        "description" => "s%c%s%c%s",
        "packaging_type" => "PC"
    ]
];

function groupBy($array, $key) {
    $groupedData = [];
    $data = [];
    $_id = "";
    for ($i=0; $i < count($array); $i++) { 
        $row = $array[$i];
        if($row[$key] != $_id){
            if(count($data) > 0){
                $groupedData[] = $data;
            }
    
            $_id = $row[$key];
            $data = [
                $key => $_id
            ];
        }
    
        unset($row[$key]);
        $data["data"][] = $row;

        if($i == count($array) - 1){
            $groupedData[] = $data;
        }
    }
    
    return $groupedData; 
}

print_r(groupBy($data, "id"));

The results will be:

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

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

                )

        )

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

                )

        )

)

If you change the "key" parameter, it should works without changes:

print_r(groupBy($data, "shipping_no"));

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

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

                )

        )

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

                )

        )

)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRedView Question on Stackoverflow
Solution 1 - PhpxdazzView Answer on Stackoverflow
Solution 2 - PhpBabaView Answer on Stackoverflow
Solution 3 - PhpatomrcView Answer on Stackoverflow
Solution 4 - PhpAndyClawView Answer on Stackoverflow
Solution 5 - Phpuser4600953View Answer on Stackoverflow
Solution 6 - PhpJake ZView Answer on Stackoverflow
Solution 7 - PhpMahidul IslamView Answer on Stackoverflow
Solution 8 - PhpSebass van BoxelView Answer on Stackoverflow
Solution 9 - PhpAmrish PrajapatiView Answer on Stackoverflow
Solution 10 - PhprwhiteView Answer on Stackoverflow
Solution 11 - PhpAthariView Answer on Stackoverflow
Solution 12 - PhpautomatixView Answer on Stackoverflow
Solution 13 - PhpCesar NietoView Answer on Stackoverflow
Solution 14 - PhpIhor BurlachenkoView Answer on Stackoverflow
Solution 15 - Phpmohamed zakiView Answer on Stackoverflow
Solution 16 - PhpZeroCoolView Answer on Stackoverflow
Solution 17 - PhpalsatorView Answer on Stackoverflow
Solution 18 - Phpnorihide.shimataniView Answer on Stackoverflow
Solution 19 - PhpBasemView Answer on Stackoverflow