-->

PHP: Sorting custom classes, using java-like Compa

2020-05-28 11:19发布

问题:

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)

回答1:

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();


回答2:

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



回答3:

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.


回答4:

__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


标签: php sorting