5

How can I make my own custom class be sortable using sort() for example?

I've been scanning the web to find any method of making a class Comparable like in Java but without much luck. I tried implementing __equals() but without luck. I've also tried with __toString(). My class looks like this:

class Genre {
    private $genre;
    private $count;
    ...
}

I want to sort them by count which is an Integer, in descending order... ($genre is a string)

3
  • 1
    What stops you to inject your class objects into an array and then traverse the array, comparing the items? Commented Oct 5, 2011 at 11:45
  • Well first of all I think this method is by far the neatest way of solving the problem, secondly I would have to implement by own version of some fast, reliable sorting algoritm such as quicksort, which is sort of reinventing the wheel a bit, seeing as sort() would more than suffice for sorting the 23 items.. they won't increase much either, will stay around ~25 items but they might change value ($count). Commented Oct 5, 2011 at 11:53
  • Create two arrays then, one holding the objects and the other their sort order. Sort order can tell you what key of the 2nd array is holding the object. It's not difficult, and you also have only 25 items so why waste time on optimization? Commented Oct 5, 2011 at 12:00

4 Answers 4

11

You can create a custom sort method and use the http://www.php.net/manual/en/function.usort.php function to call it.

Example:

$Collection = array(..); // An array of Genre objects

// Either you must make count a public variable, or create
// an accessor function to access it
function CollectionSort($a, $b)
{
    if ($a->count == $b->count)
    {
        return 0;
    }
    return ($a->count < $b->count) ? -1 : 1;
}

usort($Collection, "CollectionSort");

If you'd like to make a more generic collection system you could try something like this

interface Sortable
{
    public function GetSortField();
}

class Genre implements Sortable
{
    private $genre;
    private $count;

    public function GetSortField()
    {
        return $count;
    }
}

class Collection
{
    private $Collection = array();

    public function AddItem($Item)
    {
        $this->Collection[] = $Item;
    }

    public function GetItems()
    {
        return $this->Collection;
    }

    public function Sort()
    {
        usort($this->Collection, 'GenericCollectionSort');
    }
}

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

$Collection = new Collection();
$Collection->AddItem(...); // Add as many Genre objects as you want
$Collection->Sort();
$SortedGenreArray = $Collection->GetItems();
Sign up to request clarification or add additional context in comments.

2 Comments

Nicely explained, thank you!! Works like a charm!! Updating my post with solution!
Have a look at Thomas's solution. It might be exactly what you're looking for.
4

maybe you can use the function "usort":

class Genre {
    private $genre;
    private $count;
    ...

    public function __construct($g, $c)
    {
       $this->genre=g;
       $this->count=c;
    }


    public static function compare($a, $b)
    {
        if ($a->count < $b->count) return -1;
        else if($a->count == $b->count) return 0;
        else return 1;
    }

    ...
}



$genres= array(
  new Genre (1, 5),
  new Genre (2, 2),
  new Genre (3, 7)
);

usort($genres, array("Genre", "compare"));

Regards Thomas

Comments

0

The simplest way to do this is to simply use usort on a method within the class that accepts as input an array of matching objects. This is example 3 in the documentation linked to above. However, this is somewhat clunky and ugly.

A better way is to create a new type of array class specific to the desired objects using ArrayAccess. This will enable you to use the custom array like you'd expect but then also be able to run arbitrary sorting methods on the array.

Under the hood, you'd likely still want to use usort, but you'd be hiding that fact behind a much nicer interface that might look like this:

$array = new GenreCollection();

$array[] = new Genre(1); // Genre's constructor is a factory that can load genres via an ID
$array[] = new Genre(2);
$array[] = new Genre(3);
$array[] = new Genre(4);
$array[] = new Genre(5);

// Example Sorts
$array->SortByName();
$array->SortByDateInvented();
$array->SortByID();
$array->SortBySubGenres(); // Arranges Genres into a hierarchy where 'Death Metal' comes with other metal after 'Heavy Metal' - Add in a nest-level value for making dropdowns and other nested lists.

Comments

-1

__toString() works for me:

class Genre
{
    private $genre;
    private $count;

    public function __construct( $genre, $count = 0 )
    {
        $this->genre = $genre;
        $this->count = $count;
    }

    public function __toString()
    {
        return $this->count . ' ' . $this->genre;
    }    
}

$collection = array(
    new Genre( 'alternative', 3 ),
    new Genre( 'jazz', 2 ),
    new Genre( 'hiphop', 1 ),
    new Genre( 'heavy metal', 1 )
);

natsort( $collection );

foreach( $collection as $genre )
{
    echo $genre . "\n";
}

Produces:

1 heavy metal
1 hiphop
2 jazz
3 alternative

2 Comments

@JeffClemens No it wouldn't: it uses a natural order sort algorithm (clue is in the name natsort()).
dont misuse magic methods. its a bad habbit that will end up in complicated bugs ...

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.