这个问题旨在作为PHP中关于数组排序的问题的参考。很容易认为您的特定情况是独特的,值得提出一个新问题,但大多数情况实际上是本页上某个解决方案的微小变化。 如果你的问题是这个问题的副本,请要求重新打开你的问题,只有当你能解释为什么它与下面所有的问题都有显著不同时。

我如何排序一个数组在PHP? 我如何排序一个复杂的数组在PHP? 如何在PHP中排序对象数组?


基本一维数组;包括多维数组,包括对象数组;包括根据一个数组对另一个数组排序 用SPL排序 稳定的排序

使用PHP现有函数的实际答案见1。,关于排序算法的学术详细答案(PHP函数实现,在非常非常复杂的情况下可能需要它),请参见2。


当前回答

如果有人想要一个更简单的解决方案来操作数组,只需使用Laravel Collection包,它有一个实现的sortBy函数,可以让你简单地按键排序。

$collection->sortBy('forename')->sortBy('surname');

也就是说,为了先按a,然后按b,然后按c排序,正确的子句应该是

sortBy('c')->sortBy('b')->sortBy('a')

https://packagist.org/packages/tightenco/collect

其他回答

这个答案是关于多列排序的,其中数组应该在每个一维元素中根据非连续索引的值进行排序。 这与多维排序不同,因为每个元素只由不同的Key=>Value对组成。

function fncCmp( array $ItmOne, array $ItmTwo ) {       ; # callback for sorting items (which are arrays) by values at specific indexes
  $strCmpOne = $ItmOne[ 'ColOne' ] . $ItmOne[ 'ColThr' ]; # build compound values
  $strCmpTwo = $ItmTwo[ 'ColOne' ] . $ItmTwo[ 'ColThr' ]; #   to compare
  return $strCmpOne <=> $strCmpTwo                      ; # pass back comparison-result
} # fncCmp

$arrDat = array(                                                       # define an array of items
  array( 'ColOne' => 'Val2', 'ColTwo' => 'Val8', 'ColThr' => 'Val6' )  #   each of which
 ,array( 'ColOne' => 'Val2', 'ColTwo' => 'Val9', 'ColThr' => 'Val4' )  #   is an
 ,array( 'ColOne' => 'Val1', 'ColTwo' => 'Val7', 'ColThr' => 'Val5' )  #   array of
)                                                                    ; #   fields
var_dump       ( $arrDat           )                                 ; # emit items before sort
$bolSrt = usort( $arrDat, 'fncCmp' )                                 ; # sort the array by comparing elements
var_dump       ( $arrDat           )                                 ; # emit items after  sort

在PHP 5.3的闭包中,也可以使用闭包来确定排序的顺序。

例如,假设$array是一个包含month属性的对象数组。

 $orderArray = array("Jan","Feb","Mar","Apr","May","June","July","Aug","Sept","Oct","Nov","Dec");

 usort($array, function($a, $b) use ($orderArray){
       return array_search($a->month, $orderArray) - array_search($b->month, $orderArray);
 }); 

稳定的排序

假设你有一个这样的数组:

['Kale', 'Kaleidoscope', 'Aardvark', 'Apple', 'Leicester', 'Lovely']

现在你只想对第一个字母排序:

usort($array, function($a, $b) {
    return strcmp($a[0], $b[0]);
});

结果是:

['Apple', 'Aardvark', 'Kale', 'Kaleidoscope', 'Lovely', 'Leicester']

那种东西不稳定!

敏锐的观察者可能已经注意到,数组排序算法(快速排序)并没有产生稳定的结果,并且相同首字母的单词之间的原始顺序没有被保留。这种情况很简单,我们应该比较整个字符串,但是让我们假设您的用例更复杂,比如在不同的字段上连续进行两次排序,它们不应该相互抵消。

施瓦兹变换

Schwartzian变换,也称为装饰-排序-不装饰习语,它使用固有的不稳定排序算法来实现稳定排序。

首先,你用另一个数组装饰每个数组元素,这个数组包含一个主键(value)和一个辅助键(它的索引或位置):

array_walk($array, function(&$element, $index) {
    $element = array($element, $index); // decorate
});

这将数组转换为:

[
    ['Kale', 0], ['Kaleidoscope', 1], 
    ['Aardvark', 2], ['Apple', 3], 
    ['Leicester', 4], ['Lovely', 5]
]

现在,我们调整比较步骤;我们再次比较第一个字母,但如果它们相同,则使用次键来保留原来的顺序:

usort($array, function($a, $b) {
    // $a[0] and $b[0] contain the primary sort key
    // $a[1] and $b[1] contain the secondary sort key
    $tmp = strcmp($a[0][0], $b[0][0]);

    if ($tmp != 0) {
        return $tmp; // use primary key comparison results
    }

    return $a[1] - $b[1]; // use secondary key
});

之后,我们进行装饰:

array_walk($array, function(&$element) {
    $element = $element[0];
});

最终结果:

['Aardvark', 'Apple', 'Kale', 'Kaleidoscope', 'Leicester', 'Lovely']

重用呢?

你必须重写比较函数来处理转换后的数组元素;你可能不想编辑你精致的比较函数,所以这里有一个比较函数的包装器:

function stablecmp($fn)
{
    return function($a, $b) use ($fn) {
        if (($tmp = call_user_func($fn, $a[0], $b[0])) != 0) {
            return $tmp;
        } else {
            return $a[1] - $b[1];
        }
    };
}

让我们使用这个函数来编写排序步骤:

usort($array, stablecmp(function($a, $b) {
    return strcmp($a[0], $b[0]);
}));

瞧!您的原始比较代码回来了。

LINQ

在. net中,LINQ经常用于排序,它提供了比比较函数更好的语法,特别是当对象需要按多个字段排序时。LINQ到PHP有几个端口,包括YaLinqo库*。有了它,数组可以用单行进行排序,而无需编写复杂的比较函数。

$sortedByName         = from($objects)->orderBy('$v->name');
$sortedByCount        = from($objects)->orderBy('$v->count');
$sortedByCountAndName = from($objects)->orderBy('$v->count')->thenBy('$v->name');

比较可以通过传递回调作为第二个参数来进一步定制,例如:

$sortedByFilenameNat  = from($objects)->orderBy('$v->filename', 'strnatcmp');

这里,'$v->count'是函数($v) {return $v->count;}(任何一个都可以使用)。这些方法链返回迭代器,迭代器可以通过在最后添加->toArray()转换为数组。

在内部,orderBy和相关方法调用适当的数组排序函数(uasort、krsort、multisort、usort等)。

LINQ包含了更多受SQL启发的方法:过滤、分组、连接、聚合等。它最适合在不依赖数据库的情况下执行对数组和对象的复杂转换。

*由我开发,请参阅自述文件以了解更多细节和与其他LINQ端口的比较

这个页面非常全面,但我想再补充一点关于宇宙飞船操作符(三向比较操作符)的强大实用程序——PHP7+的一个漂亮的孩子。

使用太空船操作符实现多个排序条件

这在减少代码膨胀和提高可读性方面取得了很大的进步。

当编写自定义sort(usort()/uasort()/uksort())函数来处理多个条件时,只需要在运算符的两侧编写平衡数组并返回结果。没有更多嵌套的条件块或多个返回。

操作符两边的元素将从左到右遍历,一次一个,一旦遇到不连接的元素或元素都进行了比较,就返回求值。

用于演示的示例数据:

$multidimArray = [
    'a' => [
        'boolean' => true,
        'natString' => 'text10',
        'object' => (object)['prop' => 2],
        'float' => -.5,
        'mixed' => []
    ],
    'b' => [
        'boolean' => true,
        'natString' => 'text12',
        'object' => (object)['prop' => 4],
        'float' => 0,
        'mixed' => null
    ],
    'c' => [
        'boolean' => false,
        'natString' => 'text100',
        'object' => (object)['prop' => 9],
        'float' => -.5,
        'mixed' => false
    ],
    'd' => [
        'boolean' => true,
        'natString' => 'text1',
        'object' => (object)['prop' => 9],
        'float' => -5,
        'mixed' => "\0"
    ],
    'e' => [
        'boolean' => false,
        'natString' => 'text2',
        'object' => (object)['prop' => 2],
        'float' => .5,
        'mixed' => ''
    ]
];

演示(为了避免Stackoverflow页面膨胀,请查看输出的演示链接):

Sorting logic: boolean DESC (false = 0, true = 1, so trues before falses) float ASC uasort($multidimArray, function($a, $b) { return [$b['boolean'], $a['float']] <=> [$a['boolean'], $b['float']]; }); Sorting logic: mixed ASC object ASC boolean ASC uasort($multidimArray, function($a, $b) { return [$a['mixed'], $a['object']->prop, $a['boolean']] <=> [$b['mixed'], $b['object']->prop, $b['boolean']]; }); Sorting logic: property count of object ASC iterability of mixed DESC natString length ASC natString ASC uasort($multidimArray, function($a, $b) { return [count(get_object_vars($a['object'])), is_iterable($a['mixed']), strlen($a['natString']), $a['natString']] <=> [count(get_object_vars($b['object'])), is_iterable($b['mixed']), strlen($b['natString']), $b['natString']]; });

这种语法允许您以优雅的方式对值、函数结果、深层嵌套数据和排序方向进行排序。这绝对值得放在你的php工具带中……用于处理非数据库数据的情况——因为SQL当然是一种更明智的技术。

在PHP7.4中,您可以自行决定对这些匿名函数使用箭头语法。与箭头语法相同的脚本。