Knowledge Builder: Sort collection of objects using any member variable

As I go further down the rabbit hole that is web-development, and break away from the path of pre-built libraries and frameworks, I just keep learning more.

A few weeks ago I finally got around to creating my own collection class - for the uninitiated, a collection is a way of creating something that behaves like an array, but with the added advantage of having inheritable and extensible methods, like an object.

But that's not really what this post is about - if you want to learn more about iterators, I will write something here soon, but if you can't wait then there's always the manual to get you started.

Get on with it!

So, I've been slowly building up the methods available to my base collection class (all to make my life easier in manipulating the data in there), and then realised that I needed some way to sort the information.

There were a few challenges here:

  1. The base collection class was used to store a number of different objects (although, each collection was consistent - all the objects in a collection where of the same class) - so there were different member variables for these objects.
  2. Sometimes I'd want to sort on one member variable (for example, an integer storing an ID code), and other times on another variable (for example, a string storing a name), and whatever else.
  3. What is the value of an object anyway? And how do you compare it?

Having made use of PHP's array_multisort() function to reorder large complex arrays before, I figured that I'd be able to tackle this on easily enough, and came up with:

<?php
 
class peerCollection implements Iterator
{
  protected $peers = array();
 
  // intentionally missing a bunch of other methods as they are not relevant
 
  public function sort($field)
  {
    $sort = array();
    foreach ($this->peers as $peer) {
      $sort[] = $peer->$field;
    }
    array_multisort($this->peers, $sort);
  }
}

Not that simple

On testing this and outputting the data, my collection of objects was resolutely unsorted!

Looking around, I figured that maybe you just couldn't sort arrays of objects like this. But there had to be a way.

Finally, I hit on usort() - which is a PHP library function which uses a callback to specify the rules for sorting.

An extremely simple example (so simple, in fact, that you could replace it with sort() not bother with the callback) of the use of usort() straight from the manual:

function cmp($a, $b)
{
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

So, all I had to worry about know was the variable search field... A couple of quick and messy moments later, and this is what I got:

<?php
 
class peerCollection implements Iterator
{
  protected $peers = array();
 
  // intentionally missing a bunch of other methods as they are not relevant
 
  public function sort($field)
  {
    usort($this->peers, create_function('$a, $b', 'return peerCollection::_sort($a, $b, "'. $field . '");'));
  }
 
  public static function _sort($a, $b, $field)
  {
    if ($a->$field == $b->$field) {
      return 0;
    }
    return $a->$field < $b->$field ? -1 : 1;
  }
}

Okay, so the example above is again rather simple - but in a matter of about 30 minutes or so, I've gotten this workable bit of code that meant that I could easily sort collections of the dozen or so objects by any of the member variables I so chose.

I'll likely have to make improvements on it over time - but I'll only make those improvements when I need to and get on with the less utilitarian aspects of my site/framework/code.

Comments

Post new comment