Here are a few considerations:
You might rename
$valueto$subarrayto be more literal about the data that it holds.Because there is no need to modify the subarray, you can access the lone value by calling
current()instead ofarray_pop().You can combine your nested conditional logic into one and eliminate the declaration of
$databy assigning directly to$temp[$groupValue][]
Just before the return:
if (!$preserveSubArrays && count(
key]) == 1) {
$temp[$groupValue][] = current(
key]);
} else {
$temp[$groupValue][] =
key];
}
I guess I should also point out that your function makes a few assumptions about the data being passed to it. There are a few ways to elicit Notices/Warnings from this function by passing an uncooperative array of data or grouping value. This may be a "tinfoil hat" observation for the scope of your question, but I thought I'd acknowledge it.
Generally, it looks likes a good cover-all function for the many StackOverflow questions that seek what this function can provide.
p.s. In a previous comment that I have since deleted, I suggested replacing unset() with another function. This was before I fully understood what this function was doing. I toyed with an approach that used array_diff_key(), but there was no advantage. unset() is sure to be the most efficient way to modify the subarray (when necessary).
Years later "re-review":
- Declare the argument and return data types to make your method a bit more strict and easier to maintain.
- Use square brace syntax for declaring an array -- it is just more concise and consistent with other square brace syntax used for pushing elements.
if(!array_key_exists($groupValue, $temp)) { $temp[$groupValue] = array(); }is unnecessary because in php you don't need to instantiate an array before you push elements into it using[]syntax. BUT, it makes good sense to check if a given row actually contains the grouping column -- else you will be trying to access an undefined key.- Obey PSR-12 coding recommendations.
- Endeavor to remove all single-use variables unless they improve the readability/comprehensibility of your script.
- When accessing the potentially unset grouping values, reliably find it using the undisturbed original array (not the copy being iterated).
Suggested rewrite:
public function groupArray(
array $arr,
string|int $group,
bool $preserveGroupKey = false,
bool $preserveSubArrays = false
): array {
$result = [];
foreach (
key => $row) {
if (!key_exists($group, $row)) {
throw new Exception("Could not group by $group on row with key $key");
}
if (!$preserveGroupKey) {
unset(
group]);
}
$result[
key][$group]][] = !$preserveSubArrays && count($row) === 1
? current(
row;
}
return $result;
}
Answer from mickmackusa on Stack ExchangeHere are a few considerations:
You might rename
$valueto$subarrayto be more literal about the data that it holds.Because there is no need to modify the subarray, you can access the lone value by calling
current()instead ofarray_pop().You can combine your nested conditional logic into one and eliminate the declaration of
$databy assigning directly to$temp[$groupValue][]
Just before the return:
if (!$preserveSubArrays && count(
key]) == 1) {
$temp[$groupValue][] = current(
key]);
} else {
$temp[$groupValue][] =
key];
}
I guess I should also point out that your function makes a few assumptions about the data being passed to it. There are a few ways to elicit Notices/Warnings from this function by passing an uncooperative array of data or grouping value. This may be a "tinfoil hat" observation for the scope of your question, but I thought I'd acknowledge it.
Generally, it looks likes a good cover-all function for the many StackOverflow questions that seek what this function can provide.
p.s. In a previous comment that I have since deleted, I suggested replacing unset() with another function. This was before I fully understood what this function was doing. I toyed with an approach that used array_diff_key(), but there was no advantage. unset() is sure to be the most efficient way to modify the subarray (when necessary).
Years later "re-review":
- Declare the argument and return data types to make your method a bit more strict and easier to maintain.
- Use square brace syntax for declaring an array -- it is just more concise and consistent with other square brace syntax used for pushing elements.
if(!array_key_exists($groupValue, $temp)) { $temp[$groupValue] = array(); }is unnecessary because in php you don't need to instantiate an array before you push elements into it using[]syntax. BUT, it makes good sense to check if a given row actually contains the grouping column -- else you will be trying to access an undefined key.- Obey PSR-12 coding recommendations.
- Endeavor to remove all single-use variables unless they improve the readability/comprehensibility of your script.
- When accessing the potentially unset grouping values, reliably find it using the undisturbed original array (not the copy being iterated).
Suggested rewrite:
public function groupArray(
array $arr,
string|int $group,
bool $preserveGroupKey = false,
bool $preserveSubArrays = false
): array {
$result = [];
foreach (
key => $row) {
if (!key_exists($group, $row)) {
throw new Exception("Could not group by $group on row with key $key");
}
if (!$preserveGroupKey) {
unset(
group]);
}
$result[
key][$group]][] = !$preserveSubArrays && count($row) === 1
? current(
row;
}
return $result;
}
Just in case you are looking for such a functionality for your database wrapper, note that it already exists in PDO, which can group results for you right off the query, thanks to the PDO::FETCH_GROUP constant that can be used in fetchAll() function.
So you can get your arrays out of the box. For the first one you could combine this mode with PDO::FETCH_COLUMN:
$data = $db->run("SELECT `group`, name FROM users")
->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_COLUMN);
whereas for the second one it would be just
$data = $db->run("SELECT `group`, name FROM users")->fetchAll(PDO::FETCH_GROUP);
Note that such a verbose notation is better then anonymous function parameters for readability.
Put yourself in place of someone who would be reading your code. For example, imagine you are reading a like this
combineArray($arr, "group", false, true);
-- would you be able to tell what does this particular call do?
So it's at least better to define named constants that contain your booleans as values and make this call
groupArray($arr, "group", GR_PRESERVE_KEYS, GR_PRESERVE_SUBARRAYS);
having GR_DROP_KEY and GR_DISBAND_SUBARRAYS in reserve. Or consider just a single parameter with bitmask arguments, just like in many PHP functions.
Best way, if you have control over building the initial array, is just set things up like that at the start as you add entries.
If not then build a temporary array to sort:
foreach ($input_arr as $key => &$entry) {
$level_arr[$entry['level']][
entry;
}
Leaves you with the form you wanted and everything referenced together.
Build the array like that in the first place though if at all possible.
You need to group them by level first
Use foreach to loop into array check if the level is the same with the previous item then group it with that array
$templevel=0;
$newkey=0;
$grouparr[$templevel]="";
foreach ($items as
val) {
if ($templevel==$val['level']){
$grouparr[$templevel][$newkey]=$val;
} else {
$grouparr[$val['level']][$newkey]=$val;
}
$newkey++;
}
print($grouparr);
The output of print($grouparr); will display like the format you hoped for
You can also try to
print($grouparr[7]);
Will display
[7] => Array (
[4] => Array (
[cust] => XT7434
[type] => standard
)
)
Or
print($grouparr[3]);
Will display
[3] => Array (
[2] => Array (
[cust] => XT8922
[type] => premier
)
[3] => Array (
[cust] => XT8816
[type] => permier
)
)
Here is a solution with demo. Might help you:
// $myArr is your origional array.
$result_arr = [];
array_walk($myArr,function($v,$k) use (&$result_arr){
$result_arr[key($v)][] = $v[key($v)];
});
print_r($result_arr);
Click Here for Demo
If your initial array is called $source, this should do it:
$result =[]; //final array
foreach($source as $data){
$type = key($data); //eg: 'Candidate' or 'User'
if(!isset($result[$type])) $result[$type]=[];
$result[$type][] = reset($data);
}
Demo here
What about this:
$Show = array();
foreach ($result_radio as $Station => $Show){
$Show [$result_radio[$Station]['StationID']][] = $Show; }
It should do exactly what you want.
you can also write a function like:
function _group_by($array, $key) {
$return = array();
foreach($array as $val) {
$return[$val[$key]][] = $val;
}
return $return;
}
$class_array = array();
foreach ($students_array as $sa) {
$class_array[$sa['class']][] = array('sid' => $sa['sid']);
}
I think your code could be rewritten as (untested):
$out = array();
foreach ($students_array as $row) {
extract($row);
if (!defined($out[$class])) {
$out[$class] = array();
}
$out[$class][] = array('sid' => $sid);
}
I believe you should be able to use array_map() to build this array but for the sake of readability I'd probably run with what you have at the moment.
foreach($files as $file){
$path_parts = pathinfo($file);
$date = date ("jS \of F, Y", filemtime($file));
if(isset($dateUploaded[$date])) {
$dateUploaded[$date][] = $path_parts['filename'];
} else {
$dateUploaded[$date] = array($path_parts['filename']);
}
}
grouping
$groups = array();
foreach ($dateUploaded as $du) {
$key = $du['Date Uploaded'];
if (!isset($groups[$key])) $groups[$key] = array();
$groups[$key][] = $du['File Name'];
}
output as lists
$ret = '';
foreach ($groups as $key=>$list)
$ret.="<ul>\n".$key."<li>".implode("</li>\n<li>",$list)."</li></ul>";
I hope this helps
public function array_group_by($array, $key) {
if (is_null($key)) return $array;
$result = array();
foreach ($array as $item) {
$group_key = $item[$key];
if (!array_key_exists($group_key, $result)) {
$result[$group_key] = array();
}
$result[$group_key][] = $item;
}
return $result;
}
so you can call array_group_by($proplist, 'state'); which should give you the required result as an array.
var_dump(array_group_by($proplist,'state');
should output
array (size=3)
'GA' =>
array (size=2)
0 =>
array (size=5)
'name' => string 'Store #1' (length=8)
'address' => string '123 Example St' (length=14)
'city' => string 'Smyrna' (length=6)
'state' => string 'GA' (length=2)
'zip' => string '11111' (length=5)
1 =>
array (size=5)
'name' => string 'Store #4' (length=8)
'address' => string '111 Someplace Rd' (length=16)
'city' => string 'Atlanta' (length=7)
'state' => string 'GA' (length=2)
'zip' => string '22222' (length=5)
'VA' =>
array (size=1)
0 =>
array (size=5)
'name' => string 'Store #2' (length=8)
'address' => string '666 Anywhere Rd' (length=15)
'city' => string 'Bristol' (length=7)
'state' => string 'VA' (length=2)
'zip' => string '33333' (length=5)
'NC' =>
array (size=1)
0 =>
array (size=5)
'name' => string 'Store #3' (length=8)
'address' => string '123 Any Street' (length=14)
'city' => string 'Bristol' (length=7)
'state' => string 'NC' (length=2)
'zip' => string '44444' (length=5)
You can also find a working example here https://eval.in/98276
As far as I know, there are no grouping build in functions to archive this. So the only way is to do it manually, like so:
$result = array();
foreach ($proplist as $data) {
$id = $data['state'];
if (isset($result[$id])) {
$result[$id][] = $data;
} else {
$result[$id] = array($data);
}
}
var_dump($result);
Or if you want only to sort those elements:
foreach ($proplist as $key => $row) {
$states[$key] = $row['state'];
}
array_multisort($states, SORT_ASC, $proplist);
Roughly like this:
$groupedItems = array();
foreach($data as $item)
{
$groupedItems[$item['property_id']][] = $item;
}
// See @Lepanto's comment below, this resets the keys to match output OP required:
$groupedItems = array_values($groupedItems);
It's better to use a function() for future re-usability:
/**
*
* Group sub-arrays ( of multidimensional array ) by certain key
* @return array
*
*/
function array_multi_group_by_key($input_array, $key, $remove_key = false, $flatten_output = false)
{
$output_array = [];
foreach ($input_array as $array) {
if ($flatten_output) {
$output_array[$array[$key]] = $array;
if ($remove_key) {
unset($output_array[$array[$key]][$key]);
}
} else {
$output_array[$array[$key]][] = $array;
if ($remove_key) {
unset($output_array[$array[$key]][0][$key]);
}
}
}
return $output_array;
}
I've written it for my project weeks ago and it's proven to be working fine. You don't have to use $remove_key nor flatten_output.
Usage:
var_dump(array_multi_group_by_key($input_array, 'property_id'));