What is the best way to sort an array of objects where you need one field sorted in one order and the others fields in another order?

In the array of objects below, I would like to sort them by "date" in descending order, then "last" (name) by ascending order, and "first" (name) in ascending order as well.

$arr = array();
$obj = new stdclass();
$obj->date   = '20080510';
$obj->first  = 'Jane';
$obj->last   = 'Doe';
$arr[] = $obj;

$obj = new stdclass();
$obj->date   = '20090125';
$obj->first  = 'John';
$obj->last   = 'Doe';
$arr[] = $obj;

$obj = new stdclass();
$obj->date   = '20080305';
$obj->first  = 'Mary';
$obj->last   = 'Jane';
$arr[] = $obj;

    You can use [man]usort/man and apply your sorting logic to the callback function you use with it.

      yes, I know about usort(). I need an example of how to handle the ascending and descending of multiple values at the same time.

        array_multisort() will do it.

        foreach ($arr as $key => $val) {
            $tmp_date[$key]  = $val->date;
            $tmp_last[$key]  = $val->last;
            $tmp_first[$key] = $val->first;
        }
        array_multisort($tmp_date, SORT_DESC, $tmp_last, SORT_ASC, $tmp_first, SORT_ASC, $arr);

          usort() option:

          function mySort($obj1, $obj2)
          {
             $diff = $obj2->date - $obj1->date;
             if($diff == 0)
             {
                $diff = strcasecmp($obj1->last, $obj2->last);
                if($diff == 0)
                {
                   $diff = strcasecmp($obj1->first, $obj2->first);
                }
             }
             return $diff;
          }
          
          usort($arr, 'mySort');
          print_r($arr);
          

            Thank you both. That's awesome.

            The PHP documentation on that array_multisort() function really needs to be improved.

              tank;10912367 wrote:

              Thank you both. That's awesome.

              The PHP documentation on that array_multisort() function really needs to be improved.

              It always makes my head hurt when I try to read it, so I just stick with the usort(). 😉

                Just to be contrary 🙂 - I think the examples for array_multisort() lay it out nicely.

                Using usort() can consume a lot less memory, I think, although I don't know what kind of temporary arrays if any it uses in the background..

                  The good news is that as my database skills have improved, I pretty much never have to do such sorting in PHP any more, as I can now usually get my queries to give me the data already sorted as needed. 🙂

                    Installer, the array_multisort() doc should go into more detailed explanations before giving code examples. It's not really clear how it works and the order of passing arguments. At first glance it's not clear that passing the last array variable ends up sorting it. It just seems like it does it by magic to me. NogDog, it gives me a headache too. 🙂

                    NogDog, I agree about using SQL sorting but it works only if all your tables are in the same database (or multiple databases on same SQL server). When you have to access data from more than one database on more than one server, you can't use SQL's sorting power. I got to combine the data and manually sort it in PHP.

                    Thanks again.

                      NogDog;10912368 wrote:

                      It always makes my head hurt when I try to read it, so I just stick with the usort(). 😉

                      I can now usually get my queries to give me the data already sorted as needed.

                      array_multisort's argument list is structured the same way as SQL's ORDER BY clause. Once that bit makes sense the rest follows. The hassle is that you have to construct the sort keys manually (ref. Installer's example) - several potentially large temp variables.

                      I still much prefer usort. That said, precomputing keys can make sense if it takes a lot of work to derive them (usort doesn't retain that from one call to the next, so every comparison requires the whole lot to be redone).

                      That said, you can then use those precomputed keys in usort.

                      That said, doing so requires a bit of massaging to get the array into and out of the form usort requires.

                      That said, sometimes there is no clear-cut "key" that can be derived for each element independently of all the others and then array_multisort simply can't be used.

                        And that said, I'm still glad I haven't had reason to use array_multisort() in quite some time. :p

                          Myself, I'm looking forward to using stuff like this regularly:

                          class HeapSorter extends SplHeap
                          {
                              public function compare($val_1, $val_2)
                              {
                                  if (!($comp = $val_1->date - $val_2->date)) {
                                      if (!($comp = strcasecmp($val_2->last, $val_1->last))) {
                                          $comp = strcasecmp($val_2->first, $val_1->first);
                                      }
                                  }
                                  return $comp;
                              }
                          }
                          
                          $heap = new HeapSorter();
                          $obj = new stdClass(); 
                          $obj->date   = '20080510'; 
                          $obj->first  = 'Jane'; 
                          $obj->last   = 'Doe'; 
                          $heap->insert($obj);
                          
                          $obj = new stdClass(); 
                          $obj->date   = '20090125'; 
                          $obj->first  = 'John'; 
                          $obj->last   = 'Doe'; 
                          $heap->insert($obj); 
                          
                          $obj = new stdClass(); 
                          $obj->date   = '20080305'; 
                          $obj->first  = 'Mary'; 
                          $obj->last   = 'Jane'; 
                          $heap->insert($obj); 
                          
                          foreach ($heap as $obj) {
                              echo $obj->first . ' ' . $obj->last . ', ' . $obj->date . '<br />';
                          }

                            Personally, I'd prefer the following two reformulations of the comparison and population.

                            function generic_thingy_compare($a, $b)
                            {
                            	if(($comp = $a->date - $b->date)) return $comp;
                            	if(($comp = strcasecmp($a->last, $b->last)) return $comp;
                            	if(($comp = strcasecmp($a->first, $b->first)) return $comp;
                            	return 0;
                            }
                            
                            $objs[] = (object)array('date' => '20080510', 'first'=>'Jane', 'last'=>'Doe');
                            $objs[] = (object)array('date' => '20090125', 'first'=>'John', 'last'=>'Doe');
                            $objs[] = (object)array('date' => '20080305', 'first'=>'Mary', 'last'=>'Jane');
                            
                            usort($objs, 'generic_thingy_compare');
                            

                            Though at this stage there's no need for the casting; that probably comes later.

                              Write a Reply...