在PHP中按对象属性对数组排序?
如果我有这样一个对象:在PHP中按对象属性对数组排序?,php,sorting,Php,Sorting,如果我有这样一个对象: class Person { var $age; function __construct($age) { $this->age = $age; } } 我有各种各样的人 是否有一种简单的方法可以按Person->age属性对$people数组进行排序?usort或uasort/*要维护索引关联,如果您在特定场景中使用关联数组*/,您可以使用usort函数对其进行排序,您可以在其中定义自己的函数来比较数组中的项 <?php class
class Person {
var $age;
function __construct($age) {
$this->age = $age;
}
}
我有各种各样的人
是否有一种简单的方法可以按Person->age属性对$people数组进行排序?usort或uasort/*要维护索引关联,如果您在特定场景中使用关联数组*/,您可以使用usort函数对其进行排序,您可以在其中定义自己的函数来比较数组中的项
<?php
class Person {
var $age;
function __construct($age) {
$this->age = $age;
}
}
function personSort( $a, $b ) {
return $a->age == $b->age ? 0 : ( $a->age > $b->age ) ? 1 : -1;
}
$person1 = new Person(14);
$person2 = new Person(5);
$person3 = new Person(32);
$person4 = new Person(150);
$person5 = new Person(39);
$people = array($person1, $person2, $person3, $person4, $person5);
print_r( $people );
usort( $people, 'personSort' );
print_r( $people );
问题是由于调用比较回调的开销,导致使用效率低下。这个答案着眼于使用内置排序函数和非递归快速排序实现之间的区别 自2009年以来,随着PHP的发展,答案会随着时间的推移而改变,所以我一直在更新它。旧的材料,虽然不再相关,但仍然很有趣 TL;DR:从PHP7.0.1开始,非递归快速排序不再比使用带回调的usort快。事实并非总是如此,这就是为什么下面的细节会让阅读变得有趣的原因。真正的收获是,如果你对你的问题进行基准测试并尝试其他方法,你可以得到令人惊讶的结果 2016年1月更新 现在我们发布了PHP7.0,7.1即将发布!最后,对于这个数据集,内置的usort比以前稍微快一点 2015年1月更新 当我在2009年最初回答这个问题时,我将使用usort与非递归快速排序进行了比较,看看是否有区别。事实证明,两者之间存在显著差异,quicksort的运行速度快了3倍 由于现在是2015年,我认为重新讨论这个问题可能会有用,所以我使用usort和quicksort对15000个对象进行排序的代码,并在3v4l.org上运行它,3v4l.org在许多不同的PHP版本上运行它。全部结果如下: 2009年的原始注释 我尝试了一种方法,在大约1.8秒内对15000个人的对象进行了排序 由于您担心对比较函数的调用效率低下,我将其与非递归实现进行了比较。这实际上占用了大约三分之一的时间,大约0.5秒 下面是我的代码,它对这两种方法进行了基准测试
// Non-recurive Quicksort for an array of Person objects
// adapted from http://www.algorithmist.com/index.php/Quicksort_non-recursive.php
function quickSort( &$array )
{
$cur = 1;
$stack[1]['l'] = 0;
$stack[1]['r'] = count($array)-1;
do
{
$l = $stack[$cur]['l'];
$r = $stack[$cur]['r'];
$cur--;
do
{
$i = $l;
$j = $r;
$tmp = $array[(int)( ($l+$r)/2 )];
// partion the array in two parts.
// left from $tmp are with smaller values,
// right from $tmp are with bigger ones
do
{
while( $array[$i]->age < $tmp->age )
$i++;
while( $tmp->age < $array[$j]->age )
$j--;
// swap elements from the two sides
if( $i <= $j)
{
$w = $array[$i];
$array[$i] = $array[$j];
$array[$j] = $w;
$i++;
$j--;
}
}while( $i <= $j );
if( $i < $r )
{
$cur++;
$stack[$cur]['l'] = $i;
$stack[$cur]['r'] = $r;
}
$r = $j;
}while( $l < $r );
}while( $cur != 0 );
}
// usort() comparison function for Person objects
function personSort( $a, $b ) {
return $a->age == $b->age ? 0 : ( $a->age > $b->age ) ? 1 : -1;
}
// simple person object
class Person {
var $age;
function __construct($age) {
$this->age = $age;
}
}
//---------test internal usort() on 15000 Person objects------
srand(1);
$people=array();
for ($x=0; $x<15000; $x++)
{
$people[]=new Person(rand(1,100));
}
$start=microtime(true);
usort( $people, 'personSort' );
$total=microtime(true)-$start;
echo "usort took $total\n";
//---------test custom quicksort on 15000 Person objects------
srand(1);
$people=array();
for ($x=0; $x<15000; $x++)
{
$people[]=new Person(rand(1,100));
}
$start=microtime(true);
quickSort( $people );
$total=microtime(true)-$start;
echo "quickSort took $total\n";
以下是使用_utoString进行排序的代码:
$size=10000;
class Person {
var $age;
function __construct($age) {
$this->age = $age;
$this->sortable=sprintf("%03d", $age);
}
public function __toString()
{
return $this->sortable;
}
}
srand(1);
$people=array();
for ($x=0; $x<$size; $x++)
{
$people[]=new Person(rand(1,100));
}
$start=microtime(true);
sort( $people, SORT_STRING);
$total=microtime(true)-$start;
echo "sort($size) took $total\n"
我不建议在您的示例中使用我的解决方案,因为它会很难看,而且我没有对它进行基准测试,但它是有效的。。。。根据需要,它可能会有所帮助
class Person
{
public $age;
function __construct($age)
{
$this->age = $age;
}
public function __toString()
{
return $this->age;
}
}
$person1 = new Person(14);
$person2 = new Person(5);
$persons = array($person1, $person2);
asort($persons);
以下是值0…256的实现:
function radixsort(&$a)
{
$n = count($a);
$partition = array();
for ($slot = 0; $slot < 256; ++$slot) {
$partition[] = array();
}
for ($i = 0; $i < $n; ++$i) {
$partition[$a[$i]->age & 0xFF][] = &$a[$i];
}
$i = 0;
for ($slot = 0; $slot < 256; ++$slot) {
for ($j = 0, $n = count($partition[$slot]); $j < $n; ++$j) {
$a[$i++] = &$partition[$slot][$j];
}
}
}
由于基数排序是一种非比较排序算法,因此此操作仅在上花费。是。如果在person对象中实现,所有正常的php数组函数都能正常工作。一个观察结果是,如果数据源来自数据库,使用SQL进行排序可能比在php中更快。当然,如果数据源来自CSV或XML文件,这是没有意义的。您可以使用CSV或XML文件 请注意,堆的目的是始终具有有序集合,而不是替换usort 拥有堆的另一个好处是能够使用它们的比较函数回调其他排序函数,如usort。您只需记住,比较的顺序是颠倒的,因此使用堆进行的任何比较都将导致usort中的顺序颠倒
usort适用于中小型集合,在这些集合中,您将在最后进行一次排序。当然,使用usort不需要堆。您也可以为排序添加任何其他有效回调。您只需要编写一个自定义比较函数,然后使用类似的方法来执行实际排序。例如,如果成员变量是myVar,则可以按如下方式对其进行排序:
function cmp($a, $b)
{
if ($a->myVar == $b->myVar) {
return 0;
}
return ($a->myVar < $b->myVar) ? -1 : 1;
}
usort($myArray, "cmp");
试试usort:
例如:
<?php
function cmp($obja, $objb)
{
$a = $obja->sortField;
$b = $objb->sortField;
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$a = array( /* your objects */ );
usort($a, "cmp");
?>
如果所有相关的成员变量都保证不同,那么创建一个由这些值索引的新集合,然后对其进行排序,将更简单、更快: 如果存在重复的值,您仍然可以通过利用排序的一个鲜为人知的特性来避免usort,即数组的数组按第一个标量成员的值排序
foreach($obj_list as $obj)
$map[] = array($obj->some_var, $obj);
sort($map); // sorts $map by the value of ->some_var
我想这仍然比usort快10000000倍。我采用了以下方法:创建一个接受对象数组的函数,然后在函数内部创建一个关联数组,使用属性作为数组的键,然后使用ksort对数组键进行排序:
class Person {
var $age;
function __construct($age) {
$this->age = $age;
}
}
function sortPerson($persons = Array()){
foreach($persons as $person){
$sorted[$person->age] = $person;
}
ksort($sorted);
return array_values($sorted);
}
$person1 = new Person(14);
$person2 = new Person(5);
$persons = array($person1, $person2);
$person = sortPerson($persons);
echo $person[0]->age."\n".$person[1]->age;
/* Output:
5
14
*/
我刚刚把这个编码了。它应该比usort更快,因为它不依赖于大量函数调用
function sortByProp($array, $propName, $reverse = false)
{
$sorted = [];
foreach ($array as $item)
{
$sorted[$item->$propName][] = $item;
}
if ($reverse) krsort($sorted); else ksort($sorted);
$result = [];
foreach ($sorted as $subArray) foreach ($subArray as $item)
{
$result[] = $item;
}
return $result;
}
用法:
$sorted = sortByProp($people, 'age');
哦,它使用了ksort,但即使有许多$人的年龄相同,它也能工作。您可以使用:
以下是一个考虑以下因素的选项: 名称空间 私有财产 使用getter和setter方法 排序为参数的属性 PHP
我尽量避免使用usort,因为随着我的人员阵列的增长,通话成本太高。让我们假设我有15000个$people条目。你认为usort有什么效率低下的地方,你会
e通过任何其他方法避免?usort将进行适当的排序,应该非常有效。它将在每次调用时生成一个函数,这使得它在大型数据集中效率低下。我已经发布了一些基准详细信息-usort还不错,但使用非递归快速排序确实可以加快速度。你在做什么,需要一次对15000个对象进行排序?我尽量避免使用usort,因为随着我的人员数组的增长,调用usort的成本太高。假设我在$people中有15000个条目,我想你可以更简单地实现personSort:返回$a->age-$b->age;呵呵,真的吗?我只是太习惯这样做了,我并没有真正从数学上想出来。@DonKirkby return$a->age-$b->age;使用浮点数产生错误的结果,您可以使用spaceship操作符,因为PHP7.0和它一起使用浮点数返回$a->age$b->age;你检查过算法的正确性了吗?您需要将$array[$i]<$tmp by$array[$i]>age<$tmp->age、$tmp<$array[$j]by$tmp->age<$array[$j]>age和$i->age age更改$i哦,如果您想比较这两种算法,您应该在相同的数据上运行它们,而不仅仅是在相同大小的数据上运行,而是在完成不同特征的数据上运行。生成您的人员数组一次,并使用两种算法对相同数据的副本进行排序。关于使用相同数据进行测试的备注如何?在比较算法时,您仍然在使用不同的测试数据。您还应该验证每个函数的结果是否相同。@Gumbo,我在每次测试之前都为随机数生成器添加种子,以便重复运行相同的测试。我也在结果中添加了一个健全性检查,但在这个答案中忽略了它。1000次是无底洞的夸张。如果两个或多个实例的两个或多个属性是相同的呢?我认为缓慢性来自这样一个事实,即这仍然会导致每个对象调用一个函数,现在它只是在类本身中。
function cmp($a, $b)
{
if ($a->myVar == $b->myVar) {
return 0;
}
return ($a->myVar < $b->myVar) ? -1 : 1;
}
usort($myArray, "cmp");
<?php
function cmp($obja, $objb)
{
$a = $obja->sortField;
$b = $objb->sortField;
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$a = array( /* your objects */ );
usort($a, "cmp");
?>
foreach($obj_list as $obj)
$map[$obj->some_var] = $obj;
ksort($map);
/// $map now contains the sorted list
foreach($obj_list as $obj)
$map[] = array($obj->some_var, $obj);
sort($map); // sorts $map by the value of ->some_var
class Person {
var $age;
function __construct($age) {
$this->age = $age;
}
}
function sortPerson($persons = Array()){
foreach($persons as $person){
$sorted[$person->age] = $person;
}
ksort($sorted);
return array_values($sorted);
}
$person1 = new Person(14);
$person2 = new Person(5);
$persons = array($person1, $person2);
$person = sortPerson($persons);
echo $person[0]->age."\n".$person[1]->age;
/* Output:
5
14
*/
function sortByProp($array, $propName, $reverse = false)
{
$sorted = [];
foreach ($array as $item)
{
$sorted[$item->$propName][] = $item;
}
if ($reverse) krsort($sorted); else ksort($sorted);
$result = [];
foreach ($sorted as $subArray) foreach ($subArray as $item)
{
$result[] = $item;
}
return $result;
}
$sorted = sortByProp($people, 'age');
$result = Arrays::sort(array($person1, $person2), Comparator::compareBy('age'));
namespace Dummy;
class Person {
private $age;
function __construct($age) {
$this->setAge($age);
}
public function getAge()
{
return $this->age;
}
public function setAge($age)
{
$this->age = $age;
}
}
class CustomSort{
public $field = '';
public function cmp($a, $b)
{
return strcmp($a->{'get'.ucfirst($this->field)}(), $b->{'get'.ucfirst($this->field)}());
}
public function sortObjectArrayByField($array, $field)
{
$this->field = $field;
usort($array, array("Dummy\CustomSort", "cmp"));
return $array;
}
}
$robert = new Person(20);
$peter = new Person(12);
$robin = new Person(44);
$people = array($robert, $peter, $robin);
var_dump( $people );
$customSort = new CustomSort();
$people = $customSort->sortObjectArrayByField($people, 'age');
var_dump( $people );