2

So I'm not exactly sure if the title fits it best, but here's what the array looks like:

array (
   [0] => array (
              [category] => 'Value_1'
              [date]     => '01/01/2011'
              [data]     => 'A'
          )
   [1] => array (
              [category] => 'Value_3'
              [date]     => '01/01/2000'
              [data]     => 'B'
          )
   [2] => array (
              [category] => 'Value_2'
              [date]     => '01/01/2011'
              [data]     => 'D'
          )
   [3] => array (
              [category] => 'Value_2'
              [date]     => '01/01/2010'
              [data]     => 'A'
          )
   [4] => array (
              [category] => 'Value_2'
              [date]     => '01/01/2011'
              [data]     => 'C'
          )
)

How I'd like this data to be sorted would be the following:

  1. Preserve the order of the categories
  2. Within the categories, order by date DESC
  3. If dates appear multiple times, order by data alphabetically ASC

The example array would then be sorted to array ([0], [1], [4], [2], [3]), more specifically:

array (
   [0] => array (
              [category] => 'Value_1'
              [date]     => '01/01/2011'
              [data]     => 'A'
          )
   [1] => array (
              [category] => 'Value_3'
              [date]     => '01/01/2000'
              [data]     => 'B'
          )
   [2] => array (
              [category] => 'Value_2'
              [date]     => '01/01/2011'
              [data]     => 'C'
          )
   [3] => array (
              [category] => 'Value_2'
              [date]     => '01/01/2011'
              [data]     => 'D'
          )
   [4] => array (
              [category] => 'Value_2'
              [date]     => '01/01/2010'
              [data]     => 'A'
          )
)

My issue is I know I'll need usort and/or array_multisort(), but I'm not sure exactly sure how to efficiently iterate through a loop in order to sort with the criteria I gave.

2
  • Can you show us the desired output of the array above? Commented Nov 3, 2011 at 15:59
  • I went ahead and added some clarity. Commented Nov 3, 2011 at 16:25

2 Answers 2

3

Your problem can easily be solved with a nice comparison function and uasort() or usort(). Here is how it works:

Your comparison function accepts two parameters, which are elements of the array you sort. Return -1 if the first parameter should appear first in the sorted array, return 1 if the second parameter should appear first, and return 0 if both are considered equal in the sorting order.

Your order criteria is like: Sort by category. If category is the same, sort by date. If date is the same, sort by data.

Unfortunately your array is not properly structured. What if another category=Value_3 appears as the last element in the array. Should it be grouped with the other Value_3 entries, or should it be sorted alone? Depending on this answer, the array should be restructured for easier sorting.

Another improvement is the way the date is stored. America date format is completely unusable for sorting, neither as a string nor as a numeric value. Either transform it to a unix timestamp, or use ISO date format "YYYY-MM-DD". Both can easily be compared without further ado.

1

Supposing your array in $data variable, try this:

$data = Array(
    0 => array(
        "category" => 'Value_1',
        "date" => '01/01/2011',
        "data" => 'A'
    ),
    1 => array(
        "category" => 'Value_3',
        "date" => '01/01/2000',
        "data" => 'B'
    ),
    2 => array(
        "category" => 'Value_2',
        "date" => '01/01/2011',
        "data" => 'D'
    ),
    3 => array(
        "category" => 'Value_2',
        "date" => '01/01/2010',
        "data" => 'A'
    ),
    4 => array(
        "category" => 'Value_2',
        "date" => '01/01/2011',
        "data" => 'C'
    )
);

$sorted = false;
foreach ($data as $index => $row) {
    $data[$index]['date'] = strtotime($data[$index]['date']);
}
while (!$sorted) {
    $aux = null;
    $prevCat = null;
    $prevDate = null;
    $prevData = null;
    foreach ($data as $index => $row) {
        if ($prevCat != $row['category']) {
            $prevCat = $row['category'];
            $prevDate = $row['date'];
            $prevData = $row['data'];
            continue;
        } else {
            if ($row['date'] > $prevDate) {
                $sorted = false;
                $aux = $data[$index - 1];
                $data[$index - 1] = $row;
                $data[$index] = $aux;
                break;
            }
            if ($row['date'] == $prevDate && $row['data'] < $prevData) {
                $sorted = false;
                $aux = $data[$index - 1];
                $data[$index - 1] = $row;
                $data[$index] = $aux;
                break;
            }
            $prevCat = $row['category'];
            $prevDate = $row['date'];
            $prevData = $row['data'];
        }
    }
    $sorted = ($aux == null);
}
foreach ($data as $index => $row)
    $data[$index]['date'] = date("m/d/Y", $data[$index]['date']);
var_dump($data);

// outputs

array(5) {
    [0] => array(3) {
        ["category"] => string(7) "Value_1"
        ["date"] => string(10) "01/01/2011                                                               
        ["data"] => string(1) "A"
    }
    [1] => array(3) {
        ["category"] => string(7) "Value_3"
        ["date"] => string(10) "01/01/2000"
        ["data"] => string(1) "B"
    }
    [2] => array(3) {
        ["category"] => string(7) "Value_2"
        ["date"] => string(10) "01/01/2011"
        ["data"] => string(1) "C"
    }
    [3] => array(3) {
        ["category"] => string(7) "Value_2"
        ["date"] => string(10) "01/01/2011"
        ["data"] => string(1) "D"
    }
    [4] => array(3) {
        ["category"] => string(7) "Value_2"
        ["date"] => string(10) "01/01/2010"
        ["data"] => string(1) "A"
    }
}
7
  • i think you should apply this sort for each amount of date. You create an array containing the variety of dates and the sort by category and then data. After that, merge the arrays by date. Commented Nov 3, 2011 at 16:24
  • This works, and I accepted the answer; however, by adding a key between 0 and 1 (renumbering them) array ( "category" => 'Value_1', "date" => '01/02/2011', "data" => 'B') it no longer works. I'll try to figure it out. Commented Nov 3, 2011 at 17:39
  • ops... Just move $prevDate and others inside the loop! Look my code now Commented Nov 3, 2011 at 17:53
  • Also, you need to add && $index > 0 to the if statements that could be effected from reading out of bounds data. Commented Nov 3, 2011 at 17:58
  • @CharlesSmith When $index == 0 then $prevData and others are null, which are different from $row['data'] and so on... Commented Nov 3, 2011 at 18:02

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.