按列值对关联数组的数组进行排序

603

考虑以下数组:

$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),

);

我该怎么做呢?


3
可能是如何在 PHP 中对多维数组进行排序的重复问题。 - Jon
1
为什么不直接重构你的输入数组,让 price 列排在第一位,type 列排在第二位呢?这样,你就可以直接调用 rsort() 函数了。https://3v4l.org/2meqs - mickmackusa
排序规范 - mickmackusa
23个回答

799

您是正确的,您要查找的函数是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);

6
虽然这肯定比其他选择更昂贵。 - Matt
9
更贵?很奇怪,在我的机器上(运行 PHP 5.3.1-dev),array_multisort() 在小数组上比较快,而在大数组上(100+ 元素)速度高达快了 100 倍。 - Josh Davis
3
不需要做任何更改就能与数字键一起使用。如果您在使用数字键时遇到了错误或奇怪的行为,请以新问题的形式发布它。 - Josh Davis
5
array_multisort有一个大问题:它不能保持原始键(key)。 - machineaddict
2
@machineaddict 它确实会维护关联键。 - Matej Svajger
显示剩余7条评论

510

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正常工作:

从比较函数返回非整数值(如浮点数)将导致回调函数的返回值进行内部转换为整数。因此,诸如0.99和0.1之类的值都将被转换为整数值0,这将将这些值视为相等。

这是在使用 PHP 5.x 中 usort 时需要记住的一个重要陷阱!我的原始版本答案犯了这个错误,但我在成千上万的浏览量中获得了十个赞,显然没有人注意到这个严重的错误。像我这样的笨蛋很容易搞砸比较函数,这正是在 PHP 7 中添加更易于使用的太空船操作符的原因。

15
抱歉,但这种方法会删除关联数组中的字符串键。应该使用“uasort”函数代替。 - Matteo-SoftNet
13
有趣 - 我之前不知道uasort。不过看了文档后,在这种情况下,这个答案仍然是正确的。在 OP 的例子中,要排序的数组具有顺序数字索引而不是字符串索引,因此 usort 更加合适。在顺序索引数组上使用 uasort 将导致排序后的数组未按其数值索引排序,因此在 foreach 循环中看到的第一个元素不是 $your_array[0],这可能不是期望的行为。 - Mark Amery
通过使用从JSON文件中提取的数字键(即文章ID)和PHP 8x,我使用了uasort和太空船运算符来获取信息,以便不修改键值(因此值保持其原始文章ID)。请参见https://www.php.net/manual/en/array.sorting.php以查找维护键关联的方法。感谢这个非常有用的答案。 - G-Cyrillus

160

虽然其他人已经正确地建议使用array_multisort(),但出于某种原因,似乎没有一个答案承认array_column()的存在,它可以极大地简化解决方案。所以我的建议是:

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

如果您希望对字符串进行不区分大小写排序,可以使用SORT_NATURAL|SORT_FLAG_CASE

array_multisort(array_column($inventory, 'key_name'), SORT_DESC, SORT_NATURAL|SORT_FLAG_CASE, $inventory);

3
由于某些原因,我无法使其适用于包含小写/大写字母的字符串。即使使用了SORT_FLAG_CASE。以下方法对我进行了字符串比较:array_multisort( array_map(strtolower, array_column($ipr_projects, 'Name')), SORT_ASC, $ipr_projects); - Pabamato
良好运行的 PHP 7.4 - Bang Nguyen

46

由于你的数组元素本身也是具有字符串键的数组,最好的方法是定义一个自定义比较函数。这很快并且很容易实现。试试这个:

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和inline匿名函数),可以得到一行代码:uasort($inventory, function ($a, $b) {if ($a==$b) return 0; else return ($a > $b) ? -1 : 1;}); 这行代码的作用是对 $inventory 数组进行排序,排序规则是将值较大的元素排在前面。其中 function ($a, $b) 是一个匿名函数,用于比较两个元素的大小。 - Alan Porter
@AlanPorter 对于具有连续数字键的数组进行排序,使用usort似乎比uasort更合适。得到一个第一个元素在索引1处,第二个元素在索引0处的数组是奇怪的行为,并且对于不熟悉PHP数组细节的人来说是一个陷阱;usort会给出您直观预期的输出。 - Mark Amery

30
我完成了这个:

我结束了这个:

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');

2
@MarkAmery,我更喜欢包含在函数中的答案。它鼓励复制粘贴者使用函数,并希望写出不那么混乱的代码。 - Goose
在临时的$sortarray中保留$key是不必要的。这个答案可以简化为一行,不需要自定义函数声明和显式的方向常量使用。array_multisort(array_column($array, $subfield), $array); - undefined

21

您可以使用匿名函数(例如)usort


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

版本 PHP 5 >= 5.5.0,PHP 7,对于像我这样真的想让它为自己工作的人。 - Matt P
1
引人注目的是,旨在比较字符串的 strnatcmp 竟然在这里能够正常工作。 显然它实现的“自然顺序”包括将数字字符串按数字而不是字典排序。 - Mark Amery
太空船操作符执行相同类型的比较,而无需迭代函数调用。 - undefined

17

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

通过使用usort (http://php.net/usort),我们可以按升序和降序对数组进行排序。只需创建一个函数并将其作为参数传递给usort即可。如下面的示例所示,如果我们传递小于条件,则按降序排序。 例如 :

$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'];
}

usort($array, "cmp");
print_r($array);

输出:

Array
 (
    [0] => Array
        (
            [price] => 200.0
            [product] => test3
        )

    [1] => Array
        (
            [price] => 1000.50
            [product] => test1
        )

    [2] => Array
        (
            [price] => 8800.50
            [product] => test2
        )
  )

1
这个答案出现在低质量审核队列中,可能是因为您没有提供任何关于代码的解释。如果这段代码回答了问题,请考虑在您的答案中添加一些解释代码的文字。这样,您更有可能获得更多的赞同 - 并帮助提问者学习新知识。 - lmo
1
-1;这里的cmp函数是错误的。它应该返回“如果第一个参数被认为小于、等于或大于第二个参数,则返回小于零、等于零或大于零的整数”,但实际上返回truefalse。不过,令人惊讶的是,它似乎仍然能够工作——可能是因为当前的usort和相关函数的实现将“小于”和“等于”情况视为相同的——但不要指望它在未来的PHP版本中继续工作。如果他们试图使排序稳定(即不会不必要地移动相等的元素),这将会破坏它。 - Mark Amery
此外,在这里使用usortuasort更为合适,因为uasort会保留键和值之间的关联,而在处理顺序数字数组时,这会令人困惑和意外。例如,调用uasort后,上面的$array索引分别为2、0和1。除非你有某种原因需要这样做,否则你可能更愿意使用usort,它会重新索引数组并重新排序。 - Mark Amery
在PHP7+中:在cmp函数中,应该使用“太空船”操作符<=>。 - ddruganov

14
$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

8

适用于PHP 7及更高版本。

/**
 * A method for sorting associative arrays by a key and a direction.
 * Direction can be ASC or DESC.
 *
 * @param $array
 * @param $key
 * @param $direction
 * @return mixed $array
 */
function sortAssociativeArrayByKey($array, $key, $direction){

    switch ($direction){
        case "ASC":
            usort($array, function ($first, $second) use ($key) {
                return $first[$key] <=> $second[$key];
            });
            break;
        case "DESC":
            usort($array, function ($first, $second) use ($key) {
                return $second[$key] <=> $first[$key];
            });
            break;
        default:
            break;
    }

    return $array;
}

使用方法:

$inventory = sortAssociativeArrayByKey($inventory, "price", "ASC");

1
运行得非常顺利。 - Inderjeet

7

从PHP 7.4开始,您可以使用箭头函数:

usort(
    $inventory, 
    fn(array $a, array $b): int => $b['price'] <=> $a['price']
);

代码 (演示):

$inventory = [
    ['type' => 'fruit', 'price' => 3.50],
    ['type' => 'milk',  'price' => 2.90],
    ['type' => 'pork',  'price' => 5.43],
];

usort(
    $inventory, 
    fn(array $a, array $b): int => $b['price'] <=> $a['price']
);

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)
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接