代码之家  ›  专栏  ›  技术社区  ›  Ali

如何在PHP中对对象数组进行排序?

  •  3
  • Ali  · 技术社区  · 15 年前

    假设我有一群人,每个人都有名字、性别和年龄属性,代码如下:

    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 回复  |  直到 15 年前
        1
  •  8
  •   Thibault Martin-Lagardette    15 年前

    下面是一个使用静态方法的工作代码。它还使用这样一个事实:静态方法可以访问私有的ivar:) 它还使用了PHP Awesome Reflexivity<3。

    这个代码的好处是 Person 是提供排序方法的类,从OOP的角度来看,这更好。只有班级 应该是知道如何分类的人 . 既不 People 或者另一个独立的功能应该。

    注意:不使用 is_callable() ,因为它只验证参数是否可以作为函数调用,但不检查它是否可以用当前可见性(public、private、protected)实际调用。

    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();
    
        2
  •  4
  •   Marc W    15 年前

    看一看 usort . 它允许您指定自己的比较函数。每次需要比较两个对象时,它都会调用您指定的比较函数,以查看哪个对象大于另一个对象(或者如果它们相等)。在比较函数中,您可以对这两个字段执行任何需要的操作。 Person 对象进行比较。

    对于使用类方法进行回调(如您的示例中所示),请查看 passing callbacks . 例如,您可以这样做:

    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() 给你的 班级。

    对于静态方法,它可能如下所示:

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

    如你所见,它看起来非常相似。我不建议您使用静态方法。这更凌乱,违反了单一责任原则。

        3
  •  1
  •   Kamil Szot    15 年前

    尤其是如果getname()是耗时的操作,那么最好使用 装饰分类未装饰 模式。

    <?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的数组进行排序。排序期间的比较是使用内置的comparator完成的,所以应该很快。在 使用权 方法自定义比较器在排序过程中被多次调用(超过n次),可能会降低速度。