如何在PHP中按给定键的值对关联数组进行排序?


446

给定此数组:

$inventory = array(

   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),
   array("type"=>"pork", "price"=>5.43),

);

我想$inventory按价格对元素进行排序以获得:

$inventory = array(

   array("type"=>"pork", "price"=>5.43),
   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),

);

我怎样才能做到这一点?


Answers:


605

没错,您要寻找的功能是array_multisort()

以下是直接取自手册并适合您的情况的示例:

$price = array();
foreach ($inventory as $key => $row)
{
    $price[$key] = $row['price'];
}
array_multisort($price, SORT_DESC, $inventory);

从PHP 5.5.0开始,您可以使用array_column()代替foreach

$price = array_column($inventory, 'price');

array_multisort($price, SORT_DESC, $inventory);

5
尽管这绝对比其他选择贵。
马特

5
更贵吗?奇怪的是,在我的机器上(运行PHP 5.3.1-dev),array_multisort()在小型阵列上快几个百分点,在大型阵列(100个以上元素)上快100倍
Josh Davis 2009年

3
它不需要任何更改即可使用数字键。如果遇到与数字键有关的错误或怪异行为,请将其发布为新问题。
乔什·戴维斯

4
array_multisort有一个大问题:它不维护原始密钥。
machineaddict

1
@machineaddict它确实维护关联密钥。
Matej Svajger 2015年

317

PHP 7+

由于PHP 7的,这可以用简洁来完成usort匿名函数使用的飞船操作比较元素。

您可以像这样进行升序排列:

usort($inventory, function ($item1, $item2) {
    return $item1['price'] <=> $item2['price'];
});

或者像这样的降序排序:

usort($inventory, function ($item1, $item2) {
    return $item2['price'] <=> $item1['price'];
});

要了解其工作原理,请注意,它usort采用了用户提供的比较功能,该功能必须具有以下功能(来自文档):

如果第一个参数被认为分别小于,等于或大于第二个参数,则比较函数必须返回小于,等于或大于零的整数。

还要注意<=>,飞船操作员

如果两个操作数相等,则返回0;如果左边较大,则返回1;如果右边较大,则返回-1。

这正是usort需要的。实际上,添加<=>https://wiki.php.net/rfc/combined-comparison-operator中的语言所提供的全部理由几乎就是

使编写订购回调usort()更加容易


PHP 5.3以上

PHP 5.3引入了匿名函数,但是还没有太空飞船操作符。我们仍然usort可以对数组进行排序,但是它有点冗长,更难以理解:

usort($inventory, function ($item1, $item2) {
    if ($item1['price'] == $item2['price']) return 0;
    return $item1['price'] < $item2['price'] ? -1 : 1;
});

请注意,尽管处理整数值的比较器只返回值的差(例如)是很普遍的,但在这种情况下$item2['price'] - $item1['price'],我们不能安全地做到这一点。这是因为在问问者的示例中,价格是浮点数,但是传递给我们的比较函数usort必须返回整数usort才能正常工作:

从比较函数返回非整数值(例如float)将导致内部转换为回调返回值的整数。因此,诸如0.99和0.1的值都将强制转换为整数值0,这会将这些值进行比较。

usort在PHP 5.x中使用时,这是一个重要的陷阱!这个答案的原始版本犯了这个错误,但是我显然在没有人注意到这个严重错误的情况下,对数千个视图进行了十次投票。像我这样的笨拙者可以轻松搞定比较器功能,这正是在PHP 7中向该语言添加了更易于使用的太空飞船运算符的原因。


8
抱歉,但是这种方法会从关联数组中删除字符串键。应该使用“ uasort”功能。
Matteo-SoftNet 2014年

8
@DotMat有趣-我不知道uasort。在查看了文档之后,在这种情况下,这个答案仍然是正确的。在OP的示例中,要排序的数组具有顺序数字索引,而不是字符串索引,因此usort更合适。使用uasort顺序排列的索引阵列上将会导致其不受其数字索引,使得在看到的第一元件订购了排序后的数组foreach循环不是$your_array[0],这是不太可能是所期望的行为。
Mark Amery 2014年

99

尽管其他人正确建议使用array_multisort(),但由于某种原因,似乎没有答案可以肯定的存在array_column(),这可以大大简化解决方案。所以我的建议是:

array_multisort(array_column($inventory, 'price'), SORT_DESC, $inventory);

1
由于某种原因,我无法使其与具有低位/高位字母的字符串一起使用。即使使用SORT_FLAG_CASE。以下为我的字符串比较工作:array_multisort(array_map(strtolower,array_column($ ipr_projects,'Name')),SORT_ASC,$ ipr_projects);
Pabamato

8
这是最优雅的答案。应该被评为更高!
Armin Hierstetter '18年

3
最短和最简单的一种,我认为可以接受的一种
StudioX

1
好东西 为我完美地工作!
Funk Doc

像魅力一样工作,ty
Leif_Lundberg '19

42

由于数组元素本身就是带有字符串键的数组,因此最好的选择是定义一个自定义比较函数。这是非常快速和容易做到的。尝试这个:

function invenDescSort($item1,$item2)
{
    if ($item1['price'] == $item2['price']) return 0;
    return ($item1['price'] < $item2['price']) ? 1 : -1;
}
usort($inventory,'invenDescSort');
print_r($inventory);

产生以下内容:

Array
(
    [0] => Array
        (
            [type] => pork
            [price] => 5.43
        )

    [1] => Array
        (
            [type] => fruit
            [price] => 3.5
        )

    [2] => Array
        (
            [type] => milk
            [price] => 2.9
        )

)

4
结合此处的其他一些评论(uasort和内联匿名函数),您将获得此单线内容:uasort( $inventory, function ($a, $b) { if ( $a==$b ) return 0; else return ($a > $b) ? -1 : 1; });
艾伦·波特

@AlanPorter usort似乎比uasort使用顺序数字键对数组排序更合适。最后得到一个数组,第一个元素在索引处1,第二个元素在索引处,这0是奇怪的行为,对于那些不熟悉PHP数组细节的人们来说,这无疑是一个陷阱。usort提供您直觉上期望的输出。
Mark Amery

25

我到此结束:

function sort_array_of_array(&$array, $subfield)
{
    $sortarray = array();
    foreach ($array as $key => $row)
    {
        $sortarray[$key] = $row[$subfield];
    }

    array_multisort($sortarray, SORT_ASC, $array);
}

只需调用该函数,并传递数组和第二级数组的字段名称即可。喜欢:

sort_array_of_array($inventory, 'price');

1
哈!我只是做几乎完全相同的事情,打算发表,但是看到你的...被赞了。
罗布·埃文斯

1
拒绝投票,因为这与乔什·戴维斯(Josh Davis)几年前发布的解决方案完全相同。
Mark Amery

不同意...我不是说这是一个不同的解决方案,我只是说我以这个解决方案结束,并提供了完整的工作功能。
Danielzt

1
@MarkAmery我更喜欢函数中包含的答案。它鼓励复制粘贴程序使用功能,并希望编写更少的意大利面条代码。

19

您可以使用usort匿名功能,例如

usort($inventory, function ($a, $b) { return strnatcmp($a['price'], $b['price']); });

版本PHP 5> = 5.5.0,PHP 7对于那些你喜欢我,真的希望这能为他们工作..
马特·P

1
值得注意的是strnatcmp,用于比较字符串的似乎在这里可以正常工作。显然,它实现的“自然顺序”包括按数字方式而不是按词法对数字字符串进行排序。
Mark Amery

10
$inventory = 
    array(array("type"=>"fruit", "price"=>3.50),
          array("type"=>"milk", "price"=>2.90),
          array("type"=>"pork", "price"=>5.43),
          );

function pricesort($a, $b) {
  $a = $a['price'];
  $b = $b['price'];
  if ($a == $b)
    return 0;
  return ($a > $b) ? -1 : 1;
}

usort($inventory, "pricesort");
// uksort($inventory, "pricesort");

print("first: ".$inventory[0]['type']."\n\n");
// for usort(): prints milk (item with lowest price)
// for uksort(): prints fruit (item with key 0 in the original $inventory)

// foreach prints the same for usort and uksort.
foreach($inventory as $i){
  print($i['type'].": ".$i['price']."\n");
}

输出:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9

6

按php中给定键的值对关联数组的数组进行排序

uasort(http://php.net/uasort)允许您通过自己定义的函数对数组进行排序。就您而言,这很简单:

$array = array(
  array('price'=>'1000.50','product'=>'test1'),
  array('price'=>'8800.50','product'=>'test2'),
  array('price'=>'200.0','product'=>'test3')
);

function cmp($a, $b) {
  return $a['price'] > $b['price'];
}

uasort($array, "cmp");

1
这个答案出现在低质量的审核队列中,大概是因为您没有提供任何代码说明。如果此代码回答了问题,请考虑在回答中添加一些解释该代码的文本。这样,您更有可能获得更多的赞誉-并帮助提问者学习新的知识。
lmo

1
嗯。这是最好的答案。
commonpike

1
-1; cmp这里的功能是错误的。它应该返回“如果第一个参数分别小于,等于或大于第二个参数,则小于,等于或大于零的整数”,而是返回truefalse。尽管如此,它似乎仍然可以正常工作-也许是因为usort和朋友的当前实现对“小于”和“等于”的情况一视同仁-但不要指望它在将来的PHP版本中继续工作。如果他们试图使排序保持稳定(即不要不必要地在相等的元素周围移动),则会破坏这种情况。
Mark Amery

而且,usort比起uasort这里,它更合适,因为uasort保留了键和值之间的关联,这在使用顺序数值数组处理时会造成混淆和意外。例如,$array调用后的above 的索引uasort依次为2、0和1。除非出于某种原因,否则您可能会更喜欢使用usort,它可以对数组重新索引以及对其重新排序。
Mark Amery

5

已在10万条记录上进行了测试: 时间以秒为单位(由功能微秒计算)。 仅用于对键位置进行排序的唯一值。

@Josh Davis函数的解决方案: 花费时间:1.5768740177155

矿井解决方案: 花费时间:0.094044923782349

解:

function SortByKeyValue($data, $sortKey, $sort_flags=SORT_ASC)
{
    if (empty($data) or empty($sortKey)) return $data;

    $ordered = array();
    foreach ($data as $key => $value)
        $ordered[$value[$sortKey]] = $value;

    ksort($ordered, $sort_flags);

    return array_values($ordered); *// array_values() added for identical result with multisort*
}

7
但是,唯一排序键的要求有点破坏交易。如果您有可以作为键的唯一排序值,那么就会提出一个问题:为什么不简单地使用这些键来构造数组呢?在OP的情况下,很难想象两个价格相同的商品是不可能的。请记住,使用此解决方案将导致数组中的项目神秘地,无声地从排序后的结果集中消失。
克里斯·贝克

@克里斯·贝克,你是对的。这仅适用于唯一值。但是此解决方案的运行速度非常快,因此速度是制造和使用它的原因。目前可能是不实际的,需要使用PHP 7.1.x进行测试。
Nefelim

4

我用uasort这样的

<?php
$users = [
    [
        'username' => 'joe',
        'age' => 11
    ],
    [
        'username' => 'rakoto',
        'age' => 21
    ],
    [
        'username' => 'rabe',
        'age' => 17
    ],
    [
        'username' => 'fy',
        'age' => 19
    ],    
];


uasort($users, function ($item, $compare) {
    return $item['username'] >= $compare['username']; 
});

var_dump($users);

3

此功能可重复使用:

function usortarr(&$array, $key, $callback = 'strnatcasecmp') {
    uasort($array, function($a, $b) use($key, $callback) {
        return call_user_func($callback, $a[$key], $b[$key]);
    });
}

默认情况下,它可以很好地处理字符串值,但是如果您所有的值都是数字,则必须为数字比较函数添加回调。


您调用此方法usortarr,然后调用uasort而不是usort; 也许有点混乱。后者是-在具有数字索引的顺序数组的情况下(如问题中显示的那样)-可能实际上是您想要的。
Mark Amery

2

您可以尝试定义自己的比较函数,然后使用usort


是。如果找不到解决方案,我会这样做。我很确定您可以将其中一些奇怪的参数添加到其中之一以完成此操作。谢谢您的想法!
马特

2

这是我很久以前发现并整理的一种方法。这很好用,并且可以快速更改为接受对象。

/**
 * A method for sorting arrays by a certain key:value.
 * SortByKey is the key you wish to sort by
 * Direction can be ASC or DESC.
 *
 * @param $array
 * @param $sortByKey
 * @param $sortDirection
 * @return array
 */
private function sortArray($array, $sortByKey, $sortDirection) {

    $sortArray = array();
    $tempArray = array();

    foreach ( $array as $key => $value ) {
        $tempArray[] = strtolower( $value[ $sortByKey ] );
    }

    if($sortDirection=='ASC'){ asort($tempArray ); }
        else{ arsort($tempArray ); }

    foreach ( $tempArray as $key => $temp ){
        $sortArray[] = $array[ $key ];
    }

    return $sortArray;

}

要更改对对象进行排序的方法,只需更改以下行:

$tempArray[] = strtolower( $value[ $sortByKey ] );$tempArray[] = strtolower( $value->$sortByKey );

要运行该方法只需

sortArray($inventory,'price','ASC');


这种方法有效,但是比Josh Davis的答案(带有array_multisort)或我的答案(带有)要简洁得多,usort并且在交换方面似乎没有任何优势。
Mark Amery

1
//Just in one line custom function
function cmp($a, $b)
{
return (float) $a['price'] < (float)$b['price'];
}
@uasort($inventory, "cmp");
print_r($inventory);

//result

Array
(
[2] => Array
    (
        [type] => pork
        [price] => 5.43
    )

[0] => Array
    (
        [type] => fruit
        [price] => 3.5
    )

[1] => Array
    (
        [type] => milk
        [price] => 2.9
    )

)

1

尝试这个:

$prices = array_column($inventory, 'price');
array_multisort($prices, SORT_DESC, $inventory);
print_r($inventory);

您好,欢迎来到stackoverflow,并感谢您的回答。尽管此代码可以回答问题,但是您可以考虑添加一些说明来解决什么问题以及如何解决该问题吗?这将帮助将来的读者更好地了解您的答案并从中学习。
Plutian

0

完整的动态功能 我跳到这里进行关联数组排序,并在http://php.net/manual/en/function.sort.php上找到了这个惊人的功能。该功能非常动态,可以使用指定的键以升序和降序排序。

通过特定键对数组排序的简单函数。保持索引关联

<?php

function array_sort($array, $on, $order=SORT_ASC)
{
    $new_array = array();
    $sortable_array = array();

    if (count($array) > 0) {
        foreach ($array as $k => $v) {
            if (is_array($v)) {
                foreach ($v as $k2 => $v2) {
                    if ($k2 == $on) {
                        $sortable_array[$k] = $v2;
                    }
                }
            } else {
                $sortable_array[$k] = $v;
            }
        }

        switch ($order) {
            case SORT_ASC:
                asort($sortable_array);
            break;
            case SORT_DESC:
                arsort($sortable_array);
            break;
        }

        foreach ($sortable_array as $k => $v) {
            $new_array[$k] = $array[$k];
        }
    }

    return $new_array;
}

$people = array(
    12345 => array(
        'id' => 12345,
        'first_name' => 'Joe',
        'surname' => 'Bloggs',
        'age' => 23,
        'sex' => 'm'
    ),
    12346 => array(
        'id' => 12346,
        'first_name' => 'Adam',
        'surname' => 'Smith',
        'age' => 18,
        'sex' => 'm'
    ),
    12347 => array(
        'id' => 12347,
        'first_name' => 'Amy',
        'surname' => 'Jones',
        'age' => 21,
        'sex' => 'f'
    )
);

print_r(array_sort($people, 'age', SORT_DESC)); // Sort by oldest first
print_r(array_sort($people, 'surname', SORT_ASC)); // Sort by surname

-1
$arr1 = array(

    array('id'=>1,'name'=>'aA','cat'=>'cc'),
    array('id'=>2,'name'=>'aa','cat'=>'dd'),
    array('id'=>3,'name'=>'bb','cat'=>'cc'),
    array('id'=>4,'name'=>'bb','cat'=>'dd')
);

$result1 = array_msort($arr1, array('name'=>SORT_DESC);

$result2 = array_msort($arr1, array('cat'=>SORT_ASC);

$result3 = array_msort($arr1, array('name'=>SORT_DESC, 'cat'=>SORT_ASC));


function array_msort($array, $cols)
{
    $colarr = array();
    foreach ($cols as $col => $order) {
    $colarr[$col] = array();
    foreach ($array as $k => $row) { $colarr[$col]['_'.$k] = strtolower($row[$col]); }
}

$eval = 'array_multisort(';

foreach ($cols as $col => $order) {
    $eval .= '$colarr[\''.$col.'\'],'.$order.',';
}

$eval = substr($eval,0,-1).');';
eval($eval);
$ret = array();
foreach ($colarr as $col => $arr) {
    foreach ($arr as $k => $v) {
        $k = substr($k,1);
        if (!isset($ret[$k])) $ret[$k] = $array[$k];
        $ret[$k][$col] = $array[$k][$col];
    }
}
return $ret;


} 

尽管此代码段可以解决问题,但提供说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。也请尽量不要在代码中添加解释性注释,因为这会降低代码和解释的可读性!
再见StackExchange

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.