【问题标题】:How to sort an array of objects in PHP?如何在 PHP 中对对象数组进行排序?
【发布时间】:2010-12-04 10:54:29
【问题描述】:

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

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 类?

【问题讨论】:

    标签: php arrays oop sorting


    【解决方案1】:

    看看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() 添加到您的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());
        }
    }
    

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

    【讨论】:

    • 很有趣,但是在包含类的文件中为此创建一个函数会非常难看。有没有办法传递像People::comparePeople() 这样的函数,所以排序函数是People 类的一部分,而不是一个独立的函数?
    • 或者更好:return strcmp($p1->name, $p2->name); ;)
    • @ClickUpvote:你可以传递 People::comparePeople(不过你可能必须声明它是静态的)
    • 我现在正在编辑我的答案,以包括一个关于如何使用非静态执行此操作的示例。
    • 好的答案已编辑。没有检查语法,但应该没问题。诀窍是 call_user_func()。
    【解决方案2】:

    这是一个带有静态方法的工作代码。它还利用了静态方法可以访问私有 ivars 的事实 :) 它还使用了 PHP 令人敬畏的反射性

    关于该代码的好处是Person 是提供排序方法的类,从OOP 的角度来看,这更好。只有类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();
    

    【讨论】:

      【解决方案3】:

      特别是如果 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 更快,因为它只调用了 n 次 getName() 来对长度为 n 的数组进行排序。排序期间的比较是使用内置比较器完成的,因此应该很快。在usort 方法中,自定义比较器在排序期间被调用多次(超过n 次),它可能会减慢速度。

      【讨论】:

      • 很丑。想解释一下这段代码是如何工作的?
      • 解释是“不是对对象数组进行排序,而是对数组数组进行排序,最后一个元素是对象,第一个元素是要排序的键。排序后只保留对象。”跨度>
      • 我试图详细说明这种排序机制。如果您在理解方面有进一步的问题,请通过提出具体问题告诉我。
      猜你喜欢
      • 2019-11-02
      • 2017-05-01
      • 1970-01-01
      • 2021-10-21
      • 2015-04-12
      • 1970-01-01
      • 2021-04-15
      • 1970-01-01
      相关资源
      最近更新 更多