php 在PHP中按对象属性对数组进行排序?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1462503/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sort array by object property in PHP?
提问by Paul Dixon
If I have an object as such:
如果我有这样的对象:
class Person {
var $age;
function __construct($age) {
$this->age = $age;
}
}
and I have any array of Persons
我有任何Persons数组
$person1 = new Person(14);
$person2 = new Person(5);
$people = array($person1, $person2);
Is there an easy way to sort the $peoplearray by the Person->ageproperty?
有没有一种简单的方法可以$people按Person->age属性对数组进行排序?
回答by Paul Dixon
The question was concerned about the inefficiency of using usortbecause of the overhead of calling the comparison callback. This answer looks at the difference between using the built-in sort functions and a non-recursive quicksort implementation.
问题是担心由于调用比较回调的开销,使用usort的效率低下。这个答案着眼于使用内置排序函数和非递归快速排序实现之间的区别。
The answer changed over time as PHP evolved since 2009, so I've kept it updated. The older material, while no longer relevant, is still interesting though!
随着 PHP 自 2009 年以来的发展,答案随着时间的推移而改变,所以我一直在更新它。较旧的材料虽然不再相关,但仍然很有趣!
TL;DR: as of php 7.0.1, a non-recursive quicksort is no longer faster than using usort with a callback.This wasn't always the case, which is why the details below make interesting reading. The real takeaway is that if you benchmark your problem and try alternative approaches, you can come up with surprising results.
TL;DR:从 php 7.0.1 开始,非递归快速排序不再比使用带回调的 usort 快。情况并非总是如此,这就是为什么下面的细节读起来很有趣。真正的收获是,如果您对问题进行基准测试并尝试替代方法,您可以得出令人惊讶的结果。
Jan 2016 update
2016 年 1 月更新
Well here we are with php 7.0 released and 7.1 on the way! Finally, for this dataset, the built-in usort is ever-so-slightlyfaster!
好吧,我们已经发布了 php 7.0,并且 7.1 即将发布!最后,对于这个数据集,内置的 usort速度要稍微快一点!
+-----------+------------+------------+------------+------------+------------+
| Operation | HHVM | php7.0.1 | php5.6.3 | 5.4.35 | 5.3.29 |
+-----------+------------+------------+------------+------------+------------+
| usort | *0.0445 | *0.0139 | 0.1503 | 0.1388 | 0.2390 |
| quicksort | 0.0467 | 0.0140 | *0.0912 | *0.1190 | *0.1854 |
| | 5% slower | 1% slower | 40% faster | 15% faster | 23% faster |
+-----------+------------+------------+------------+------------+------------+
Jan 2015 update
2015 年 1 月更新
When I originally answered this in 2009, I compared using usort with a non-recursive quicksort to see if there was a difference. As it turned out, there was significantdifference, with the quicksort running 3x faster.
当我最初在 2009 年回答这个问题时,我将使用 usort 与非递归快速排序进行了比较,以查看是否存在差异。事实证明,存在显着差异,快速排序的运行速度提高了 3 倍。
As it's now 2015, I thought it might be useful to revisit this, so I took code which sorts 15000 objects using usort and quicksort and ran it on 3v4l.org which runs it on lots of different PHP versions. The full results are here: http://3v4l.org/WsEEQ
现在是 2015 年,我认为重新审视这个可能会很有用,所以我使用了使用 usort 和 quicksort 对 15000 个对象进行排序的代码,并在 3v4l.org 上运行它,它在许多不同的 PHP 版本上运行它。完整结果在这里:http: //3v4l.org/WsEEQ
+-----------+------------+------------+------------+------------+------------+
| Operation | HHVM | php7alpha1 | php5.6.3 | 5.4.35 | 5.3.29 |
+-----------+------------+------------+------------+------------+------------+
| usort | *0.0678 | 0.0438 | 0.0934 | 0.1114 | 0.2330 |
| quicksort | 0.0827 | *0.0310 | *0.0709 | *0.0771 | *0.1412 |
| | 19% slower | 30% faster | 25% faster | 31% faster | 40% faster |
+-----------+------------+------------+------------+------------+------------+
Original Notes from 2009
2009 年的原始笔记
I tried a usort, and sorted 15000 Person objects in around 1.8 seconds.
我尝试了一个usort,并在大约 1.8 秒内对 15000 个 Person 对象进行了排序。
As you are concerned about the inefficiency of the calls to the comparison function, I compared it with a non-recursive Quicksortimplementation. This actually ran in around one third of the time, approx 0.5 seconds.
由于您担心对比较函数的调用效率低下,我将其与非递归Quicksort实现进行了比较。这实际上运行了大约三分之一的时间,大约 0.5 秒。
Here's my code which benchmarks the two approaches
这是我的代码,它对两种方法进行了基准测试
// 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";
An interesting suggestion was to add a __toStringmethod to the class and use sort(), so I tried that out too. Trouble is, you must pass SORT_STRING as the second parameter to sort get it to actually call the magic method, which has the side effect of doing a string rather than numeric sort. To counter this, you need to pad the numbers with zeroes to make it sort properly. Net result was that this was slower than both usort and the custom quickSort
一个有趣的建议是__toString在类中添加一个方法并使用 sort(),所以我也尝试了。麻烦的是,您必须将 SORT_STRING 作为第二个参数传递给 sort 以使其实际调用魔术方法,这具有执行字符串而不是数字排序的副作用。为了解决这个问题,您需要用零填充数字以使其正确排序。最终结果是这比 usort 和自定义 quickSort 都慢
sort 10000 items took 1.76266698837
usort 10000 items took 1.08757710457
quickSort 10000 items took 0.320873022079
Here's the code for the sort() using __toString():
这是使用 __toString() 的 sort() 代码:
$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"
回答by meder omuraliev
For that specific scenario, you can sort it using the usort() function, where you define your own function to compare the items in the array.
对于该特定场景,您可以使用 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 );
回答by Gordon
You could either use usortor a heap.
class SortPeopleByAge extends SplMaxHeap
{
function compare($person1, $person2)
{
return $person1->age - $person2->age;
}
}
$people = array(new Person(30), new Person(22), new Person(40));
$sorter = new SortPeopleByAge;
array_map(array($sorter, 'insert'), $people);
print_r(iterator_to_array($sorter)); // people sorted from 40 to 22
Note that the purpose of an Heap is to have an ordered collection at all times and not to replace usort. For large collections (1000+), a heap will be faster and less memory intensive though.
请注意,堆的目的是在任何时候都有一个有序的集合,而不是替换usort. 对于大型集合(1000+),堆会更快,内存占用更少。
An added benefit of having Heaps is being able to use their comparison function for callbacks to other sorting functions, like usort. You just have to remember that the order for the comparison is reversed, so any comparison done with a Heap will result in reversed order in usort.
拥有堆的另一个好处是能够使用它们的比较函数来回调其他排序函数,例如usort. 你只需要记住,比较的顺序是颠倒的,所以对堆进行的任何比较都会导致usort.
// using $people array and $sorter
usort($people, array($sorter, 'compare'));
print_r($people); // people sorted from 22 to 40
usortis fine for small to medium collections where you will do the sorting once at the end. Of course, you dont have to have a heap to use usort. You can just as well add any other valid callback for the sorting.
usort适用于中小型集合,您将在最后进行一次排序。当然,你不必有一个堆来使用usort。您也可以为排序添加任何其他有效的回调。
回答by Camilo Martin
I just coded this. It should be faster than usortas it does not rely on numerous function calls.
我刚刚编码了这个。它应该比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;
}
Usage:
用法:
$sorted = sortByProp($people, 'age');
Oh, and it uses ksortbut it works even if many $peopleare of the same $age.
哦,它使用ksort但即使许多$people是相同的,它也能工作$age。
回答by Paul Dixon
You just need to write a custom comparison function, then use something like usortto do the actual sorting. For example, if the member variable was myVar, you could sort it as follows:
您只需要编写一个自定义比较函数,然后使用类似usort 的东西来进行实际排序。例如,如果成员变量是myVar,则可以按如下方式对其进行排序:
function cmp($a, $b)
{
if ($a->myVar == $b->myVar) {
return 0;
}
return ($a->myVar < $b->myVar) ? -1 : 1;
}
usort($myArray, "cmp");
回答by woru
You can do it with ouzo goodies:
你可以用茴香酒来做:
$result = Arrays::sort(array($person1, $person2), Comparator::compareBy('age'));
http://ouzo.readthedocs.org/en/latest/utils/comparators.html
http://ouzo.readthedocs.org/en/latest/utils/comparators.html
回答by xmc
I went with the following approach: created a function that takes an array of objects, then inside the function I create an associative array using the property as key for the array, then sort they array keys using ksort:
我采用了以下方法:创建了一个接受对象数组的函数,然后在函数内部创建一个关联数组,使用该属性作为数组的键,然后使用 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
*/
回答by adamsch1
One observation is that if the source of the data is from a database, it's probably faster to sort using SQL than it would be within PHP. Of course this is moot if the data source is from a CSV or XML file.
一种观察结果是,如果数据源来自数据库,则使用 SQL 进行排序可能比在 PHP 中排序要快。当然,如果数据源来自 CSV 或 XML 文件,这没有实际意义。
回答by Toto
I do not advice my solution in your example because it would be ugly (And I have not benchmarked it), but it works.... And depending of the need, it may help. :)
我不建议在您的示例中使用我的解决方案,因为它会很丑(而且我没有对其进行基准测试),但是它有效......并且根据需要,它可能会有所帮助。:)
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);
回答by Gumbo
Here's a stableRadix Sortimplementation for values 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];
}
}
}
This costs only O(n) since Radix Sort is a non-comparing sorting algorithm.
由于基数排序是一种非比较排序算法,因此这仅花费O( n)。

