Как отсортировать массив объектов в PHP? - PullRequest
3 голосов
/ 07 октября 2009

Скажем, у меня есть группа людей, у каждого из которых есть имя, пол и возраст, закодированные так:

public class Person
{
   private $name, $sex, $age;
   public function Person ($name, $sex, $age)
   {
       $this->name = $name;
       $this->sex = $sex;
       $this->age = $age;
   }
   public function getName();    
   public function getSex();
   public function getAge();
}

public class People
{
   private $people = array();
   public function addPerson ($name, $sex, $age)
   {
      $this->people[] = new Person($name, $sex, $age);
   }
}

Как я могу реализовать метод sortPeople(), который сортировал массив $people в порядке возрастания имен людей в класс People?

Ответы [ 3 ]

8 голосов
/ 07 октября 2009

Вот рабочий код со статическим методом. Он также использует тот факт, что статический метод может получить доступ к приватным ivars :) Он также использует PHP удивительную рефлексивность <3. </p>

Хорошим моментом в этом коде является то, что Person - это класс для предоставления метода сортировки, который лучше с точки зрения ООП. Только класс Person должен знать, как сортировать другие Person. Ни People, ни другая независимая функция не должны.

Примечание: не используется is_callable(), поскольку он только проверяет, можно ли вызвать параметр как функцию, но не проверяет, действительно ли он вызывается с текущей видимостью (публичной, приватной, защищенной)

class Person
{
    private $name, $sex, $age;
    public function Person($name, $sex, $age)
    {
        $this->name = $name;
        $this->sex = $sex;
        $this->age = $age;
    }

    public static function sortByName(Person $p1, Person $p2)
    {
        return strcmp($p1->name, $p2->name);
    }

    public static function sortByAge(Person $p1, Person $p2)
    {
        return ($p1->age - $p2->age);
    }
}

class People
{
    private $people = array();
    public function addPerson($name, $sex, $age)
    {
        $this->people[] = new Person($name, $sex, $age);
    }

    public function display()
    {
        print_r($this->people);
    }

    public function sort($attribute = 'name')
    {
        $sortFct = 'sortBy' . ucfirst(strtolower($attribute));
        if (!in_array($sortFct, get_class_methods('Person')))
        {
            throw new Exception('People->sort(): Can\'t sort by ' . $attribute);
        }
        usort($this->people, 'Person::' . $sortFct);
    }
}

$people = new People;
$people->addPerson('Steve', 'M', 31);
$people->addPerson('John', 'M', 24);
$people->addPerson('Jane', 'F', 26);
$people->addPerson('Sally', 'F', 21);
$people->display();
$people->sort();
$people->display();
$people->sort('age');
$people->display();
4 голосов
/ 07 октября 2009

Взгляните на usort . Позволяет указать собственную функцию сравнения. Каждый раз, когда нужно сравнивать два объекта, он будет вызывать указанную вами функцию сравнения, чтобы увидеть, какой из них больше другого (или если они равны). В вашей функции сравнения вы можете делать все, что вам нужно, с полями в двух Person объектах для их сравнения.

Для выполнения обратных вызовов с методами класса (как в вашем примере) посмотрите на прохождение обратных вызовов . Например, вы можете сделать что-то вроде этого:

class People {
    // your previously defined stuff here...

    public function sort() {
        usort($this->people, array($this, 'comparePeople'));
    }

    public function comparePeople(Person $p1, Person $p2) {
        return strcmp($p1->getName(), $p2->getName());
    }
}

Вам также, конечно, нужно добавить getName() в класс Person.

Для статического подхода это может выглядеть примерно так:

function sortPeople($people) {
    usort($people, array('People', 'comparePeople'));
}

class People {
    // your previously defined stuff here...

    public static function comparePeople(Person $p1, Person $p2) {
        return strcmp($p1->getName(), $p2->getName());
    }
}

Как видите, это выглядит очень похоже. Я бы не рекомендовал вам использовать статический подход. Это грязнее и нарушает принцип единственной ответственности.

1 голос
/ 07 октября 2009

Особенно, если getName () занимает много времени, вам лучше использовать шаблон decorate-sort-undecorate .

<?php

class Person {
    private $name;
    function getName() {
      return $this->name;
    }
    function __construct($name) {
      $this->name = $name;
    }
}

$people = array(
    new Person('Jim'),
    new Person('Tom'),
    new Person('Tim'),
    new Person('Adam')
);

// actual sorting below
$people = array_map(create_function('$a', 'return array($a->getName(), $a);'), $people); // transform array of objects into array of arrays consisted of sort key and object
sort($people); // sort array of arrays
$people = array_map('end', $people); // take only last element from each array

print_r($people);

Как это работает?

Вместо сортировки массива объектов вы сортируете массив массивов, последним элементом которых является объект, а первым - ключ, по которому вы хотите отсортировать. После сортировки вы сохраняете только объект.

Вы можете использовать только sort для сортировки массивов массивов, поскольку PHP сравнивает два массива одинаковой длины, сравнивая его элементы один за другим.

Сортировка по нескольким полям

Вы можете использовать более одного ключа сортировки, например, сортировать по фамилии, и если фамилии идентичны, учитывайте имя. Вы можете сделать это, украсив несколько ключей в порядке важности, например:

$people = array_map(create_function('$a', 'return array($a->getSurname(), $a->getName(), $a);'), $people); 

Почему это быстро

Этот способ может быть быстрее, чем при использовании usort, поскольку он вызывает getName () только n раз для сортировки массива длины n. Сравнения во время сортировки выполняются с использованием встроенного компаратора, поэтому должны быть быстрыми. В usort метод пользовательского компаратора вызывается несколько раз (более n раз) во время сортировки, и это может привести к замедлению работы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...