I have a multidimensional array which needs to be sorted by two custom criteria in order:
Documentation of array_multisort()
and uksort()
have gotten me half way there each, but I am unable to put it all together meaningfully. Thank you for your time
Array
(
[name] => hashbrowns
[day] => monday
[mealTime] => breakfast
)
Array
(
[name] => Steak
[day] => monday
[mealTime] => dinner
)
Array
(
[name] => Avacados
[day] => tuesday
[mealTime] => dinner
)
Array
(
[name] => Peaches
[day] => tuesday
[mealTime] => lunch
)
Array
(
[name] => Sammich
[day] => monday
[mealTime] => lunch
)
Array
(
[name] => Kale & Sadness
[day] => tuesday
[mealTime] => breakfast
)
Desired output example:
Array
(
[name] => hashbrowns
[day] => monday
[mealTime] => breakfast
)
Array
(
[name] => Sammich
[day] => monday
[mealTime] => lunch
)
Array
(
[name] => Steak
[day] => monday
[mealTime] => dinner
)
Array
(
[name] => Kale & Sadness
[day] => tuesday
[mealTime] => breakfast
)
Array
(
[name] => Peaches
[day] => tuesday
[mealTime] => lunch
)
Array
(
[name] => Avacados
[day] => tuesday
[mealTime] => dinner
)
try this solution. Surely it can be done with php functions but what happened to you seems to work.
$data = array(
array("name" => "Otra cosa","day" => "thursday","mealTime" => "dinner"),
array("name" => "hashbrowns","day" => "monday","mealTime" => "breakfast"),
array("name" => "Steak","day" => "monday","mealTime" => "dinner"),
array("name" => "Avacados","day" => "tuesday","mealTime" => "dinner"),
array("name" => "Cuchu","day" => "monday","mealTime" => "breakfast"),
array("name" => "Avacados 2","day" => "monday","mealTime" => "lunch"));
$day = array("monday" => 0, "tuesday" => 1, "wednesday" => 2, "thursday" => 3, "friday" => 4, "saturday" => 5, "sunday" => 6);
$mealTime = array("breakfast" =>0, "lunch" =>1, "dinner" => 2);
$result = array();
foreach($data as $values) {
$d = $day[$values["day"]];
$mt = $mealTime[$values["mealTime"]];
if(!isset($result[$d])) {
$result[$d] = array();
}
if(!isset($result[$d][$mt])) {
$result[$d][$mt] = array();
}
$result[$d][$mt][] = $values;
ksort($result[$d]);
}
ksort($result);
$_result = array();
foreach($result as $r) {
foreach($r as $_r) {
foreach($_r as $__r) {
$_result[] = $__r;
}
}
}
return $_result;
//print_r("<pre>");
//print_r($_result);
//print_r("</pre>");
Very interesting question!
I have tried to abstract from it to create a generic function to order a multidimensional array by given array of criteria.
The result is the function below, which has as first parameter a multidimensional array like that provided in the question, and as second parameter a multidimensional array of associative arrays, each with the key to be sorted as key (i.e. 'day') and the desired sorted values as values (i.e. 'monday','tuesday',...). You can pass one, two or more criteria. If a row don't match the criteria, is appended at the end of resulting array; same if a row has the key criteria but their value is not in the criteria values (i.e. has the key='day' with value='everyday').
So, in your case, you have to do in this way:
$menu = //Your array//
$criteria = array
(
'day' => explode( ',', 'monday,tuesday,wednesday,thursday,friday,saturday,sunday' ),
'mealTime' => explode( ',', 'breakfast,lunch,dinner' )
);
$result = sortArrayByCriteria( $menu, $criteria );
You can see the result in this eval.in demo (I have added to your array a fake item to see the behavior when there is a non-matching criteria).
/* Sort a multidimensional array by an array of criteria which keys are
* the key to sort and values are the sort criteria
* Unmatched values are appended at the end of array.
*
* @param array $array array to be sorted
* @param array $criteria array of criteria
*
* @return array sorted array
*/
function sortArrayByCriteria( $array, $criteria )
{
# Extract current (last) criteria and reduce criteria array:
$sortValues = end( $criteria );
$sortKey = key( $criteria );
array_pop( $criteria );
$sorted = $unsorted = array();
foreach( $sortValues as $compare )
{
$key = 0;
# Check each array:
while( $key < count( $array ) )
{
# If array row doesn't have key to sort, add-it to unsorted array:
if( !isset($array[$key][$sortKey]) )
{ $unsorted[] = reset( array_splice( $array, $key, 1 ) ); }
else
{
# If value==current comparisation value, add-it to sorted:
if( $array[$key][$sortKey] == $compare )
{ $sorted[] = reset( array_splice( $array, $key, 1 ) ); }
# If value doesn't is in the comparisation values, add-it to unsorted:
elseif( !in_array( $array[$key][$sortKey], $sortValues ) )
{ $unsorted[] = reset( array_splice( $array, $key, 1 ) ); }
# Otherwise increment index pointer:
else $key++;
}
}
}
# Merge sorted and unsorted values in one array:
$retval = array_merge( $sorted, $unsorted );
# If there are others criteria, performs a self-call:
if( count( $criteria ) ) $retval = sortArrayByCriteria( $retval, $criteria );
return $retval;
}
Consider simply adding mealnumber
and daynumber
to main array, sort by those numeric fields using array_multisort()
and then remove them from array:
// INITIALIZE SORT ARRAYS
$mealarr = array();
$dayarr = array();
// APPEND TO MAIN AND SORT ARRAYS, NEW MEALNUMEBR AND DAYNUMBER ELEMENTS
for($i=0; $i<sizeof($breakfast); $i++){
foreach ($breakfast[$i] as $key => $value) {
switch ($value) {
case "breakfast": $breakfast[$i]['mealnumber'] = 1; $mealarr[] = 1; break;
case "lunch": $breakfast[$i]['mealnumber'] = 2; $mealarr[] = 2; break;
case "dinner": $breakfast[$i]['mealnumber'] = 3; $mealarr[] = 3; break;
case "monday": $breakfast[$i]['daynumber'] = 1; $dayarr[] = 1; break;
case "tuesday": $breakfast[$i]['daynumber'] = 2; $dayarr[] = 2; break;
case "wednesday": $breakfast[$i]['daynumber'] = 3; $dayarr[] = 3; break;
case "thursday": $breakfast[$i]['daynumber'] = 4; $dayarr[] = 4; break;
case "friday": $breakfast[$i]['daynumber'] = 5; $dayarr[] = 5; break;
case "saturday": $breakfast[$i]['daynumber'] = 6; $dayarr[] = 6; break;
case "sunday": $breakfast[$i]['daynumber'] = 7; $dayarr[] = 7; break;
}
}
}
// SORT MAIN ARRAY
array_multisort($dayarr, SORT_ASC, $mealarr, SORT_ASC, $data);
// REMOVE MEALNUMBER AND DAYNUMBER ELEMENTS
for($i=0; $i<sizeof($data); $i++){
foreach ($data[$i] as $key => $value) {
if ($key=='mealnumber' or $key=='daynumber') {
unset($data[$i][$key]);
}
}
}