Question

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.

Was it helpful?

Solution

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"
    }
}

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top