0

PHP multidimensional array sorting is a bit confusing to me.

What I have is an array I formed with json_decode() from a .jsonp file.

It has several variables in each primary array entry. They include "Year", "Month", "Day", "hourTimeStart", and "minuteTimeStart", along with some other information.

I'd like to sort this array by date, so I'd like to first sort by "minuteTimeStart", "hourTimeStart", "Day", then "Month", then "Year", so they're in chronological order.

The array looks like this:

Array ( 
[0] => Array ( [Year] => 2013 [Month] => February [Day] => 5 [hourTimeStart] => 5 [minuteTimeStart] => 0 [Name] => tht ) 
[1] => Array ( [Year] => 2013 [Month] => January [Day] => 6 [hourTimeStart] => 2 [minuteTimeStart] => 0 [Name] => gregre) 
[2] => Array ( [Year] => 2013 [Month] => March [Day] => 4 [hourTimeStart] => 1 [minuteTimeStart] => 15 [Name] => gregre)
)

Essentially what I'm doing is this:

$databaseFileURL = "../Appointments/AllAppointmentData.jsonp";
    if(file_exists($databaseFileURL)){
        $jsonAppointmentData = file_get_contents($databaseFileURL);
    } else $jsonAppointmentData = "";
    $AppointmentData = json_decode($jsonAppointmentData, true);

Then I want to sort $AppointmentData by the date indicated in each sub-array

8
  • 2
    This isn't a question. There's no ?. It's just a list of requirements. You'll probably want to read php.net/usort, which is what you'll probably end up using to accomplish your requirements. Commented Aug 28, 2013 at 20:19
  • possible duplicate of How do I sort a multidimensional array in php - I also removed the two distracting sentences from the start as it always works as already outlined (and we have some pretty good Q&A about the topic here on site) and even the one I linked has a nice comment below the question that is a good pointer to your date problem you face. Commented Aug 28, 2013 at 20:19
  • @hakre I don't think this has to do with multidimensional arrays, the OP doesn't know how to do a custom comparison function to take the details of the value of an array and manipulate it in some way to use it in a custom comparator. Commented Aug 28, 2013 at 20:25
  • @jbx: It is always multi-sort. Instead of comparing all pairs in that array, map the array onto sort-values (one operation per entry), then use multisort with these sort-values. Like outlined in the duplicate. Commented Aug 28, 2013 at 20:29
  • @hakre its not multi-sort, in this case it just happens to be that the values are arrays, but they could just be objects which do not have a natural ordering. The answer you are suggesting is a duplicate uses array_multisort() which only takes a few sorting criteria (ascending, descending, natural etc) and not a custom sorting function, as required here (the fields Year, Month, Day, Hour, Minute need to be combined into a value to make it comparable) Commented Aug 28, 2013 at 20:34

2 Answers 2

2

You can use usort() to provide a custom comparison function, and mktime() to build the time (in seconds since Epoch) from your Month, Day, Year, Hour, Minute parameters.

Something like this would solve your problem:

    function myDateSort($ar1, $ar2)
    {
       $month1 = //todo: convert $ar1['Month'] string to the corresponding month number
       $date1 = mktime($ar1['hourTimeStart'], $ar1['minuteTimeStart'], 0,  $month1, $ar1['Day'], $ar1['Year'] );

       $month2 = //todo: convert $ar2['Month'] string to the corresponding month number
       $date2 = mktime($ar2['hourTimeStart'], $ar2['minuteTimeStart'], 0,  $month2, $ar2['Day'], $ar2['Year'] );

       //this will sort ascending, change it to $date2 - $date1 for descending
       return $date1 - $date2;
    }

Then just do:

usort($AppointmentData, "myDateSort");
Sign up to request clarification or add additional context in comments.

14 Comments

I understand what $ar1 is, but what is $ar2?
usort will pass each of your elements into the callback function myDateSort so that you can compare them with each other. In order to compare an element with the other you need 2 arguments. So for example $ar1 would be $AppointmentData[0] while $ar2 would be $AppointmentData[1], and you must return a number less than 0 if you consider $ar1 to be less than $ar2 (by comparing their dates). usort will take care of calling them one after the other according to its internal sorting algorithm, so you dont need to loop through the elements yourself
@jbx: Your explanation is a bit ambigous. The one type of work to be done is to create the mapping function that turns what has been already understood as $ar1 into a comparable date-time value (e.g. a unix-timestamp for example). Your answer can be confused easily myDateSort being that mapping function, but it's not. It is another function that just houses the callback logic for usort. Which is related to the mapping, but more making use of it two times. You perhaps should make both more visible.
@hakre Why should it be confused with a mapping function, when I provided the links to the functions being used? There is no mention to a mapping function anywhere. I had left the implementation out for the OP to complete it and have a look at the PHP documentation to learn and not have everything spoonfed, now its complete.
@jbx: I guess he uses that object as a helper to ship the map from month names to month numerical values (this makes sense if you want to keep things independent to locale). So it needs some object(ive) states. Anyway, I didn't say it explicitly here under your answer: Yes usort is a way to solve this problem :)
|
2

First of all you have got an array of "stuff" and you want to sort that "stuff" by date.

To sort something you need to turn it into a number, so that a date in the past is a number lower than a date in the future. One value that has these properties is a UNIX timestamp.

Your stuff has the following properties that constitute stuff's date:

[Year] => 2013  [Month] => February [Day] => 5 
[hourTimeStart] => 5 [minuteTimeStart] => 0 

So you need to create a function that turns stuff's date into a timestamp. As this would be the matter of a different question (and don't worry, this has been answered before so the code is out there), I only mock such a function:

$stuffToTimestamp = function(array $stuff) {
    $timestamp = ... // do whatever needs to be done to turn 
                     // $stuff into a timestamp
    return $timestamp;
}

So now with this function at hand you are able to get a sort-able time-value for each $stuff. This is what you do now:

$sortKeys = array_map($stuffToTimestamp, $AppointmentData);

And then you sort the data:

array_multisort($AppointmentData, $sortKeys);

And that's already. $AppointmentData is now sorted.

4 Comments

+1 for O(n) calls to mktime(). This will be faster than using usort() because the function that converts the date will only be used N times, but this approach requires N extra memory for the new $sortKeys array, so depending on the number of items and the 'effort' of the mapping function one should choose one against the other.
Yes this is a trade between memory and processor (as often). But with data that has more than one property it is often as well you want to have another sort-key. This is not easily possible with usort (it is not stable), so array_multisort allows more flexibility here (with the cost of memory naturally, but that is normally not a problem, processing power is often more important than memory).
What stability issues does usort have? I think either way works fine. However, I don't know about the stability issues.
@Jamil: Stabity in the meaning of "Stable Sort Algorithm", see en.wikipedia.org/wiki/Category:Stable_sorts - This is from computer science.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.