PHP Array Merge two Arrays on same key - php

I am trying to merge the following two arrays into one array, sharing the same key:
First Array:
array(3) {
[0]=>
array(1) {
["Camera1"]=>
string(14) "192.168.101.71"
}
[1]=>
array(1) {
["Camera2"]=>
string(14) "192.168.101.72"
}
[2]=>
array(1) {
["Camera3"]=>
string(14) "192.168.101.74"
}
}
Second Array:
array(3) {
[0]=>
array(1) {
["Camera1"]=>
string(2) "VT"
}
[1]=>
array(1) {
["Camera2"]=>
string(2) "UB"
}
[2]=>
array(1) {
["Camera3"]=>
string(2) "FX"
}
}
As you can see, they share the same key (Camera1, Camera2, Camera3, etc..)
Here is what I have tried:
$Testvar = array_merge($NewArrayCam,$IpAddressArray);
foreach ($Testvar AS $Newvals){
$cam = array();
foreach($Newvals AS $K => $V){
$cam[] = array($K => $V);
}

Ideally I would look to format the two arrays in such a way that array_merge_recursive would simply merge the arrays without too much fuss.
However I did come up with a solution that used array_map.
$array1 = array(
array("Camera1" => "192.168.101.71"),
array("Camera2" => "192.168.101.72"),
array("Camera3" => "192.168.101.74"),
);
$array2 = array(
array("Camera1" => "VT"),
array("Camera2" => "UB"),
array("Camera3" => "FX")
);
$results = array();
array_map(function($a, $b) use (&$results) {
$key = current(array_keys($a));
$a[$key] = array('ip' => $a[$key]);
// Obtain the key again as the second array may have a different key.
$key = current(array_keys($b));
$b[$key] = array('name' => $b[$key]);
$results += array_merge_recursive($a, $b);
}, $array1, $array2);
var_dump($results);
The output is:
array (size=3)
'Camera1' =>
array (size=2)
'ip' => string '192.168.101.71' (length=14)
'name' => string 'VT' (length=2)
'Camera2' =>
array (size=2)
'ip' => string '192.168.101.72' (length=14)
'name' => string 'UB' (length=2)
'Camera3' =>
array (size=2)
'ip' => string '192.168.101.74' (length=14)
'name' => string 'FX' (length=2)

Try to use array_merge_recursive.

Use array_merge_recursive :
Convert all numeric key to strings, (make is associative array)
$result = array_merge_recursive($ar1, $ar2);
print_r($result);
Ref : http://php.net/array_merge_recursive

For your nesting level will be enough this:
$sumArray = array_map(function ($a1, $b1) { return $a1 + $b1; }, $array1, $array2);
For deeper nesting it wont work.

If both arrays have the same numbers of levels and keys this should work:
$array3 = array();
foreach ($array1 as $key1 => $value1) {
// store IP
$array3['Camera'.$key1]['IP'] = $value['Camera'.$key1];
// store type of cam
$array3['Camera'.$key1]['Type'] = $array2[$key]['Camera'.$key1];
}
At the end $array3 should be something like:
$array3 = array {
["Camera1"] => {['IP'] => "192.168.101.71", ['Type'] => "VT" }
["Camera2"] => {['IP'] => "192.168.101.72", ['Type'] => "UB" }
["Camera3"] => {['IP'] => "192.168.101.74", ['Type'] => "FX" }
}

this would be one of the soluion:
function array_merge_custom($array1,$array2) {
$mergeArray = [];
$array1Keys = array_keys($array1);
$array2Keys = array_keys($array2);
$keys = array_merge($array1Keys,$array2Keys);
foreach($keys as $key) {
$mergeArray[$key] = array_merge_recursive(isset($array1[$key])?$array1[$key]:[],isset($array2[$key])?$array2[$key]:[]);
}
return $mergeArray;
}
$array1 = array(
array("Camera1" => "192.168.101.71"),
array("Camera2" => "192.168.101.72"),
array("Camera3" => "192.168.101.74"),
);
$array2 = array(
array("Camera1" => "VT"),
array("Camera2" => "UB"),
array("Camera3" => "FX")
);
echo '<pre>';
print_r(array_merge_custom($array1 , $array2));

The main problem are the arrays. Because of the way they are structured it becomes unnecessarily complicated to merge them. It they simply were normal associative arrays (i.e. array('Camera1' => 'VT') then it would be effortless to merge them.
I would suggest that you figure out how to format the data in such a way as to make it easier to work with.
This is a quick and dirty way of merging the two arrays. It takes one "camera" from one array, and then tries to find the corresponding "camera" in the other array. The function only uses the "cameras" in the $ips array, and only uses matching CameraN keys.
$ips = array(
array('Camera1' => '192.168.101.71'),
array('Camera2' => '192.168.101.72'),
array('Camera3' => '192.168.101.74'),
);
$names = array(
array('Camera1' => 'VT'),
array('Camera2' => 'UB'),
array('Camera3' => 'FX'),
);
function combineCameras($ips, $names) {
$output = array();
while ($ip = array_shift($ips)) {
$ident = key($ip);
foreach ($names as $key => $name) {
if (key($name) === $ident) {
$output[$ident] = array(
'name' => array_shift($name),
'ip' => array_shift($ip),
);
unset($names[$key]);
}
}
}
return $output;
}
var_dump(combineCameras($ips, $names));

Something like this should work:
$array1 = array(array("Camera1" => "192.168.101.71"), array("Camera2" => "192.168.101.72"), array("Camera3" => "192.168.101.74"));
$array2 = array(array("Camera1" => "VT"), array("Camera2" => "UB"), array("Camera3" => "FX"));
$results = array();
foreach($array1 as $key => $array){
foreach($array as $camera => $value){
$results[$camera]['ip'] = $value;
}
}
foreach($array2 as $key => $array){
foreach($array as $camera => $value){
$results[$camera]['name'] = $value;
}
}
print_r($results);

This worked for me.
I joined two arrays with the same keys
$array1 = ArrayUtils::merge($array1, $array2);
If you need preserve NumericKey, use
$array1 = ArrayUtils::merge($array1, $array2, true);

You could convert all numeric keys to strings and use array_replace_recursive which:
merges the elements of one or more arrays together so that the values of one are appended to the end of the previous one. It returns the resulting array.
Example
$arr1 = [
'rate' => 100
];
$arr2 = [
'rate' => 100,
'name' => 'Best Name In Town',
];
print_r(array_replace_recursive($arr1, $arr2));
Output
Array
(
[rate] => 100
[name] => Best Name In Town
)

Related

Loop over associative array to generate separate section and header markup based on key value in PHP [duplicate]

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
There is no native one, just use a loop.
$result = array();
foreach ($data as $element) {
$result[$element['id']][] = $element;
}
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)
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;
}, []);
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"
}
}
}
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;
}
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;
});
$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;
}
$arr = array();
foreach($old_arr as $key => $item)
{
$arr[$item['id']][$key] = $item;
}
ksort($arr, SORT_NUMERIC);
for($i = 0 ; $i < count($arr) ; $i++ )
{
$tmpArr[$arr[$i]['id']] = $arr[$i]['id'];
}
$vmpArr = array_keys($tmpArr);
print_r($vmpArr);
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
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
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;
}
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'));
Check indexed function from Nspl:
use function \nspl\a\indexed;
$grouped = indexed($data, 'id');
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;
}
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'))
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);
}
}
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;
}
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
)
)
)
)
What about array_combine() ?
Using array_combine() stores each row on the index of $groupByColumn, so we can use that $groupByColumn as keys. This returns the last row for every group (array_combine() overwrites the value when the key already exists - see https://www.php.net/manual/en/function.array-combine.php#111668). If you want to return the first or some specific row, you can play around with array_reverse() or usort() etc.
$result = array_combine(
array_column($source, $groupByColumn),
$source
);

How to group rows of data into subarrays based on another column? [duplicate]

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
There is no native one, just use a loop.
$result = array();
foreach ($data as $element) {
$result[$element['id']][] = $element;
}
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)
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;
}, []);
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"
}
}
}
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;
}
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;
});
$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;
}
$arr = array();
foreach($old_arr as $key => $item)
{
$arr[$item['id']][$key] = $item;
}
ksort($arr, SORT_NUMERIC);
for($i = 0 ; $i < count($arr) ; $i++ )
{
$tmpArr[$arr[$i]['id']] = $arr[$i]['id'];
}
$vmpArr = array_keys($tmpArr);
print_r($vmpArr);
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
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
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;
}
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'));
Check indexed function from Nspl:
use function \nspl\a\indexed;
$grouped = indexed($data, 'id');
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;
}
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'))
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);
}
}
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;
}
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
)
)
)
)
What about array_combine() ?
Using array_combine() stores each row on the index of $groupByColumn, so we can use that $groupByColumn as keys. This returns the last row for every group (array_combine() overwrites the value when the key already exists - see https://www.php.net/manual/en/function.array-combine.php#111668). If you want to return the first or some specific row, you can play around with array_reverse() or usort() etc.
$result = array_combine(
array_column($source, $groupByColumn),
$source
);

PHP get index into new array

I have a json array like below. I need to get the index into a new array, how is this possible? Arrays are my weakness for some reason just cant grasp them. I can easily get id value, but cannot get the index (e.g 11111111). Any help would be appreciated.
Update please see the revised, my fault for not including the full multi dimensional array.
Below only outputs one result where I need all results.
<?php
$json = '[{
"11111111": {
"id": "val_somevalue5555",
"customer": {
"32312": {
"name": "jane doe"
}
}
},
"2222222": {
"id": "val_somevalue25",
"customer": {
"32312234": {
"name": "jane doe"
}
}
}
}]';
$jsonarr = json_decode($json, true);
$newarr = [];
foreach($jsonarr as $value)
{
$key = key($value);
$newarr[] = ['key' => $key, 'id' => $value[$key]['id']];
}
var_dump($newarr);
expected looped output
key 11111111
id val_somevalue5555
... looped.
You can create an array of the keys of an existing array using the array_keys() function
http://php.net/manual/en/function.array-keys.php
If you don't want the keys in a separate array, and instead just want to access them directly, when you are doing a 'foreach' loop of an array, you can choose to assign a variable to the current key by doing
foreach($jsonarr as $key => $value){...}
Because your original array is actually multidimensional (each $key has a $value that is also stored as an array of "id": "value") - this means taking one more step to get the value of key 'id':
foreach($jsonarr as $key => $value){
$newarray[] = ['key' => $key, 'id' => $value['id'];
}
you can use array_keys() or key() with a foreach loop for this(DEMO):
$newarr = [];
foreach($jsonarr as $value)
{
//$key = array_keys($value)[0];
$key = key($value);
$newarr[] = ['key' => $key, 'id' => $value[$key]['id']];
}
var_dump($newarr);
Output:
array(2) {
[0]=>
array(2) {
["key"]=>
int(11111111)
["id"]=>
string(17) "val_somevalue5555"
}
[1]=>
array(2) {
["key"]=>
int(2222222)
["id"]=>
string(15) "val_somevalue25"
}
}
Edit: With the updated json, you can use the following way, using 2 foreach loops (DEMO):
$newarr = [];
foreach($jsonarr as $json)
{
foreach($json as $key => $value)
{
$newarr[] = ['key' => $key, 'id' => $value['id']];
}
}
PHP supports a slightly different foreach syntax that extracts both the array key and the array value:
foreach ( $jsonarr as $key => $value ) {
$newarr[] = ['key' => $key, 'id' => $value];
}
Use this if you need the key ("11111111" and "2222222" in your example).
<?php
$json = '[{
"11111111": {
"id": "val_somevalue5555"
}
},
{
"2222222": {
"id": "val_somevalue25"
}
}
]';
$jsonarr = json_decode($json, true);
$newarr = [];
foreach($jsonarr as $key => $value) {
$newarr[] = ['key' => key($value), 'id' => current($value)['id']];
}
foreach($newarr as $key) {
echo 'key '.$key['key'] . PHP_EOL;
echo 'id '.$key['id'] . PHP_EOL;
}
If you remove what looks like embedded components in the $json string (otherwise it won't parse) then var_export the output of json_decode() you'll get this:
array (
0 => array (
11111111 => array (
'id' => 'val_somevalue5555',
),
),
1 => array (
2222222 => array (
'id' => 'val_somevalue25',
),
),
)
You have a double-nested array, hence...
foreach ($jsonarr as $obj) {
foreach ($obj as $name=>$value) {
print "$name = $value[id]\n";
break;
}
}
or you can reference the elements directly:
print $jsonarr[0]['11111111']['id'];
First, you are not accessing the deep enough before iterating.
If you call var_export($jsonarr); you will see:
array ( // an indexed array of subarrays
0 =>
array ( // an associative array of subarrays, access via [0] syntax (or a foreach loop that only iterates once)
11111111 => // this is the subarray's key that you want
array (
'id' => 'val_somevalue5555', // this is the value you seek from the id element of 1111111's subarray
'customer' =>
array (
32312 =>
array (
'name' => 'jane doe',
),
),
),
2222222 => // this is the subarray's key that you want
array (
'id' => 'val_somevalue25', // this is the value you seek from the id element of 2222222's subarray
'customer' =>
array (
32312234 =>
array (
'name' => 'jane doe',
),
),
),
),
)
Code: (Demo)
$jsonarr = json_decode($json, true);
$result=[];
// vvvv-avoid a function call (key()) on each iteration by declaring here
foreach($jsonarr[0] as $key=>$subarray){
// ^^^-drill down into the first level (use another foreach loop if there may be more than one)
$result[]=['key'=>$key,'id'=>$subarray['id']];
}
var_export($result);
Output:
array (
0 =>
array (
'key' => 11111111,
'id' => 'val_somevalue5555',
),
1 =>
array (
'key' => 2222222,
'id' => 'val_somevalue25',
),
)
p.s. If $jsonarr has more than one element in its first level, you should use a foreach() loop like this:
foreach($jsonarr as $array1){
foreach($array1 as $key=>$array2){
$result[]=['key'=>$key,'id'=>$array2['id']];
}
}

Creating new array from 2 existing arrays - one of them is the key other value

My "main" array looks like this - var_dump($main)
[zlec_addresoperator] => and
[filtervalue0] => Test
[filtercondition0] => CONTAINS
[filteroperator0] => 1
[filterdatafield0] => zlec_addres
[zlec_nroperator] => and
[filtervalue1] => SecondVal
[filtercondition1] => CONTAINS
[filteroperator1] => 1
[filterdatafield1] => zlec_nr
I want to build a new array as
array( filterdatafield0 = > filtervalue0 , filterdatafield1 = > filtervalue1)
etc
First of all I decided to filter out what I wan't with the following codes. Creating new arrays to keep the data I wan't, so $arraykeys will contain the filterdatafield.{1,2} values. In this case it will be zlec_addres and zlec_nr.
The second $arrayvalue will keep the filtervalue.{1,2} which is the value for the filter.
$newarray = array();
$arraykeys = array();
$arrayvalue = array();
foreach($_GET as $key => $value):
if(preg_match("/^filterdatafield.{1,2}$/",$key)>0) {
// $key is matched by the regex
$arrayvalue[] = $value;
}
if(preg_match("/^filtervalue.{1,2}$/",$key)>0) {
// $key is matched by the regex
$arraykeys[] = $key;
}
endforeach;
foreach($arraykeys as $a){
$newarray[$a] = $arrayvalue;
}
So the desired output would be
array(
zlec_addres => 'Test', zlec_nr = 'SecondVal'
)
Now it is
array(12) {
["filtervalue0"]=>
array(12) {
[0]=>
string(11) "zlec_addres"
[1]=>
string(7) "zlec_nr"
...
}
["filtervalue1"]=>
array(12) {
[0]=>
string(11) "zlec_addres"
[1]=>
string(7) "zlec_nr"
...
}
$newarray = array();
$arraykeys = array();
$arrayvalue = array();
foreach($_GET as $key => $value){
if(preg_match("/^filterdatafield.{1,2}$/",$key)>0) {
// $key is matched by the regex
$arraykeys[] = $value;
}
if(preg_match("/^filtervalue.{1,2}$/",$key)>0) {
// $key is matched by the regex
$arrayvalues[] = $value;
}
}
$newArray = array_combine($arraykeys, $arrayvalues);
This should work for you:
Just grab your keys which you want with preg_grep() and then array_combine() both arrays together.
<?php
$arr = [
"zlec_addresoperator" => "and",
"filtervalue0" => "Test",
"filtercondition0" => "CONTAINS",
"filteroperator0" => "1",
"filterdatafield0" => "zlec_addres",
"zlec_nroperator" => "and",
"filtervalue1" => "SecondVal",
"filtercondition1" => "CONTAINS",
"filteroperator1" => "1",
"filterdatafield1" => "zlec_nr",
];
$newArray = array_combine(
preg_grep("/^filterdatafield\d+$/", array_keys($arr)),
preg_grep("/^filtervalue\d+$/", array_keys($arr))
);
print_r($newArray);
?>
output:
Array
(
[filterdatafield0] => filtervalue0
[filterdatafield1] => filtervalue1
)

How to group subarrays by a column value?

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
There is no native one, just use a loop.
$result = array();
foreach ($data as $element) {
$result[$element['id']][] = $element;
}
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)
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;
}, []);
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"
}
}
}
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;
}
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;
});
$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;
}
$arr = array();
foreach($old_arr as $key => $item)
{
$arr[$item['id']][$key] = $item;
}
ksort($arr, SORT_NUMERIC);
for($i = 0 ; $i < count($arr) ; $i++ )
{
$tmpArr[$arr[$i]['id']] = $arr[$i]['id'];
}
$vmpArr = array_keys($tmpArr);
print_r($vmpArr);
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
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
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;
}
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'));
Check indexed function from Nspl:
use function \nspl\a\indexed;
$grouped = indexed($data, 'id');
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;
}
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'))
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);
}
}
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;
}
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
)
)
)
)
What about array_combine() ?
Using array_combine() stores each row on the index of $groupByColumn, so we can use that $groupByColumn as keys. This returns the last row for every group (array_combine() overwrites the value when the key already exists - see https://www.php.net/manual/en/function.array-combine.php#111668). If you want to return the first or some specific row, you can play around with array_reverse() or usort() etc.
$result = array_combine(
array_column($source, $groupByColumn),
$source
);

Categories