PHP Sort 2-D Array Elements by Date in Sub-key (Both ASC and DESC)

I have a 2-D array with hundreds of elements, with a structure like shown below.  I need to sort the array so that main elements (e.g. 0 through 999, in the example below) appear in order of the date (which appears in key 7 of each element.
    I need to give users the choice of sorting by Ascending or Descending (Least Recent or Most Recent).
    What PHP Code will sort like that quickly, and how could it be used for either ASC or DESC, per user's choice?

Array
(
    [0] => Array
        (
            [0] => 0
            [1] => Field One 0
            [2] => Field Two 0
            [3] => Field Three 0
            [4] => Field Four 0
            [5] => Field Five 0
            [6] => Field Six 0
            [7] => 2007-10-03
        )

    [1] => Array
        (
            [0] => 1
            [1] => Field One 1
            [2] => Field Two 1
            [3] => Field Three 1
            [4] => Field Four 1
            [5] => Field Five 1
            [6] => Field Six 1
            [7] => 2005-11-29
        )

   .   .   .   etc.  .  .  .

    [999] => Array
        (
            [0] => 999
            [1] => Field One 999
            [2] => Field Two 999
            [3] => Field Three 999
            [4] => Field Four 999
            [5] => Field Five 999
            [6] => Field Six 999
            [7] => 2006-12-30
        )
)
FrankTechAsked:
Who is Participating?
 
MasonWolfCommented:
My localhost testing server is 5.2.4 and it worked fine. You can see it for yourself online at http://hiremasonwolf.com/ee_test/untitled.php

The online server is version 4.4.4

Here's the exact code for that page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>

<body>
<?php
$array = array(0 => array(0 => 0,1 => 'Field One 0',2 => 'Field Two 0',3 => 'Field Three 0',4 => 'Field Four 0',5 => 'Field Five 0',6 => 'Field Six 0',7 => '2007-10-03'), 1 => array(0 => 1, 1 => 'Field One 1', 2 => 'Field Two 1', 3 => 'Field Three 1', 4 => 'Field Four 1', 5 => 'Field Five 1', 6 => 'Field Six 1', 7 => '2005-11-29'), 999 => array(0 => 999, 1 => 'Field One 999', 2 => 'Field Two 999', 3 => 'Field Three 999', 4 => 'Field Four 999', 5 => 'Field Five 999', 6 => 'Field Six 999', 7 => '2006-12-30'));
$asc = false;
usort($array, create_function('$a,$b', 'if($a[7] == $b[7]) return 0; return ($a[7] < $b[7]) ? '. (($asc) ? '-1 : 1;' : '1 : -1;')));
echo "<pre>";
var_dump($array);
echo "</pre>";
$asc = true;
usort($array, create_function('$a,$b', 'if($a[7] == $b[7]) return 0; return ($a[7] < $b[7]) ? '. (($asc) ? '-1 : 1;' : '1 : -1;')));
echo "<pre>";
var_dump($array);
echo "</pre>";
echo phpversion();
?>
</body>
</html>
0
 
Beverley PortlockCommented:
This is a little messy, but it will do what you want...





<?php
$arr = array
(
    array
        (
            '0' => '0',
            '1' => 'Field One 0',
            '2' => 'Field Two 0',
            '3' => 'Field Three 0',
            '4' => 'Field Four 0',
            '5' => 'Field Five 0',
            '6' => 'Field Six 0',
            '7' => '2007-10-03'
        ),

     array
        (
            '0' => '1',
            '1' => 'Field One 1',
            '2' => 'Field Two 1',
            '3' => 'Field Three 1',
            '4' => 'Field Four 1',
            '5' => 'Field Five 1',
            '6' => 'Field Six 1',
            '7' => '2006-12-30'
        ),

    array
        (
            '0' => '999',
            '1' => 'Field One 999',
            '2' => 'Field Two 999',
            '3' => 'Field Three 999',
            '4' => 'Field Four 999',
            '5' => 'Field Five 999',
            '6' => 'Field Six 999',
            '7' => '2005-11-29'
        )
);

echo "<pre>";print_r( $arr);"</pre>";

function cmp($a, $b)
{
     global $arr;

     $dt1 = $arr[$a]['7'];
     $dt2 = $arr[$b]['7'];

     return strcmp( $dt1, $dt2 );
}




uksort($arr, "cmp");



echo "<pre>";print_r( $arr);"</pre>";


?>
0
 
Beverley PortlockCommented:
To get DESCENDING just change the comparison round from


     return strcmp( $dt1, $dt2 );

to


     return strcmp( $dt2, $dt1 );
0
Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
MasonWolfCommented:
Here's an easier way. Just have $asc = true for ascending or false for descending:

uksort($array, create_function('$a,$b', 'if($a[7] == $b[7]) return 0; return ($a[7] < $b[7]) ? '. (($asc) ? '-1 : 1;' : '1 : -1;')));
0
 
FrankTechAuthor Commented:
Thanks. That seems to work.  I made two functions, one for ASC and one for DESC:

function cmpasc($a, $b)
{
     global $arr;
     $dt1 = $arr[$a]['7'];
     $dt2 = $arr[$b]['7'];
     return strcmp( $dt1, $dt2 ); // ASC
}

function cmpdesc($a, $b)
{
     global $arr;
     $dt1 = $arr[$a]['7'];
     $dt2 = $arr[$b]['7'];
       return strcmp( $dt2, $dt1 ); // DESC
}

Now what's the best way to allow the user to specify which of these statements to run:

     uksort($arr, "cmpasc"); // for ASC
                                              or
     uksort($arr, "cmpdesc"); // for DESC

Maybe something like this (?) at the appropriate line in the script (the place where I need the sorting to happen):

     if ($_POST['order'] == 'asc') {
        uksort($arr, "cmpasc");
    } elseif ($_POST['order'] == 'desc') {
        uksort($arr, "cmpdesc");
    } else {
       // not sorted, use raw order
    }

Is there a better or simpler way?  For example, can the sort order be sent into a single function as a parameter? If so, how?
0
 
FrankTechAuthor Commented:
I didn't see Mason Wolf's response before I posted my comment to bportlock.  A question for both of you:  which of your approaches is likely to run the fastest?  Eventually I may have thousands of elements, and it will need to run quickly and not use too much memory.  
0
 
MasonWolfCommented:
Did I not get mine up fast enough? :)

You could use a simple radiobutton with mine to specify the $asc variable:

<input type="radio" name="asc" value="1" />ASC <input type="radio" name="asc" value="0" />DESC

If you don't have register_globals on (which is probably a good idea), then just replace "$asc" in the function I provided with "$_POST['asc']".
0
 
MasonWolfCommented:
Probably mine. If you have to sort thousands of arrays, then bportlock's without question. But if you're just sorting once, even on a large array, my "fire and forget" approach to the sorting function is probably going to be quicker, though not by much.

BP? What do you think?
0
 
Beverley PortlockCommented:
If you are running it on a half-decent server, I don't think it will make any difference. I routinely handle large data sets in PHP and I'm always surprised at how quick it is for an interpreted language.
0
 
Beverley PortlockCommented:
Second thoughts - execution SPACE is more likely to be a problem than execution TIME. Many PHP installations are limited to 8MB of RAM. If you can edit php.ini and up the space limit then do so.
0
 
FrankTechAuthor Commented:
MasonWolf,
    I like the idea of creating a function dynamically, but can't get your code to work.  Here's how I'm testing it:

$asc = "TRUE"; // ASC
// $asc = "FALSE"; // DESC

uksort($arr, create_function('$a,$b', 'if($a[7] == $b[7]) return 0; return ($a[7] < $b[7]) ? '. (($asc) ? '-1 : 1;' : '1 : -1;')));

I also tried it with:
    $asc = 1; // ASC
    // $asc = 0; // DESC

The I re-named the array as $arr (instead of $array) in the function, to match my array name; and I ran the statement at the same line as BP's code.  But the sort order is not affected. What am I doing wrong?  
0
 
MasonWolfCommented:
You're using string versions for true and false. The $asc = 1 should work, but $asc = "TRUE" wouldn't.

Try:
$asc = true; //ASC
//$asc = false; //DESC

I tested my code before I posted, and that worked for me.
0
 
FrankTechAuthor Commented:
Mason Wolf,
  It still doesn't work. (Seems like the function is not running at all, as it has no effect.)  I'm not sure what else could be wrong, especially since the code tested as working before you posted it.  Maybe some difference in the PHP version ( I'm on 5.2.4 on WAMP)?
0
 
FrankTechAuthor Commented:
MasonWolf:
   It works now.  Maybe the difference is because your earlier post said "uksort", your working version uses "usort".   (I think I like usort better, anyway, as it always makes the first element 0 no matter which way the sort goes.)

bportlock:
    I think both experts' suggestions are useful, for different purposes. I'll increase and split the points.  Any comments on how to pass a sort order variable into your function, instead of having two functions and a conditional branch?  Thanks.
0
 
MasonWolfCommented:
uksort versus usort shouldn't have made a difference (I tested both). But it works, so I won't belabor the point. I'm glad your happy. Thanks for the point increase and split.
0
 
MasonWolfCommented:
By the way, you can declare a variable "$var" outside the function, and then give the function the ability to use it by placing "global $var;" inside the function at the top. That'll let you use BP's method with the ascend / descend passed in from your user rather than a conditional branch.
0
 
FrankTechAuthor Commented:
Good idea.  Thanks MasonWolf.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.