617

Given this array:

$inventory = array(

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

);

I would like to sort $inventory's elements by price to get:

$inventory = array(

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

);

How can I do this?

3

23 Answers 23

808

You are right; the function you're looking for is array_multisort().

Here's an example taken straight from the manual and adapted to your case:

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

As of PHP 5.5.0, you can use array_column() instead of that foreach:

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

array_multisort($price, SORT_DESC, $inventory);
Sign up to request clarification or add additional context in comments.

12 Comments

Though this is definitely more expensive than the alternatives.
More expensive? That's weird, on my machine (running PHP 5.3.1-dev) array_multisort() is a few percent faster on small arrays and up to 100 times faster on big arrays (100+ elements)
It shouldn't require any change to work with numeric keys. If you're hitting a bug or weird behaviour related to numeric keys, post it as a new question.
array_multisort has a big problem: it doesn't maintain the original key.
@machineaddict it does maintain the associative keys.
|
521
Answer recommended by PHP Collective

PHP 7+

As of PHP 7, this can be done concisely using usort with an anonymous function that uses the spaceship operator to compare elements.

You can do an ascending sort like this:

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

Or a descending sort like this:

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

To understand how this works, note that usort takes a user-provided comparison function that must behave as follows (from the docs):

The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.

And note also that <=>, the spaceship operator,

returns 0 if both operands are equal, 1 if the left is greater, and -1 if the right is greater

which is exactly what usort needs. In fact, almost the entire justification given for adding <=> to the language in https://wiki.php.net/rfc/combined-comparison-operator is that it

makes writing ordering callbacks for use with usort() easier


PHP 5.3+

PHP 5.3 introduced anonymous functions, but doesn't yet have the spaceship operator. We can still use usort to sort our array, but it's a little more verbose and harder to understand:

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

Note that although it's fairly common for comparators dealing with integer values to just return the difference of the values, like $item2['price'] - $item1['price'], we can't safely do that in this case. This is because the prices are floating point numbers in the question asker's example, but the comparison function we pass to usort has to return integers for usort to work properly:

Returning non-integer values from the comparison function, such as float, will result in an internal cast to integer of the callback's return value. So values such as 0.99 and 0.1 will both be cast to an integer value of 0, which will compare such values as equal.

This is an important trap to bear in mind when using usort in PHP 5.x! My original version of this answer made this mistake and yet I accrued ten upvotes over thousands of views apparently without anybody noticing the serious bug. The ease with which lackwits like me can screw up comparator functions is precisely the reason that the easier-to-use spaceship operator was added to the language in PHP 7.

3 Comments

Sorry, but this approach deletes the string keys from associative arrays. "uasort" function should be used, instead.
@DotMat Interesting - I didn't know about uasort. After looking at the docs, though, this answer is still correct in this case. In the OP's example, the array to be sorted has sequential numeric indexes rather than string indexes, so usort is more appropriate. Using uasort on a sequentially-indexed array will result in a sorted array which is not ordered by its numeric indexes, such that the first element seen in a foreach loop is not $your_array[0], which is unlikely to be desirable behaviour.
for infos, using numeric keys (which are article ids) extrated from a json file , and php 8x , i used uasort and the spaceship to not modify the key value (so value sticks to their original article's id) . See php.net/manual/en/array.sorting.php to find out, whoever needs it, which Maintains key association . Thanks for this answer that was really usefull.
167

While others have correctly suggested the use of array_multisort(), for some reason no answer seems to acknowledge the existence of array_column(), which can greatly simplify the solution. So my suggestion would be:

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

If you want Case Insensitive Sort on strings, you can use SORT_NATURAL|SORT_FLAG_CASE

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

2 Comments

For some reason I was not able to make it work with strings having lower/upper letters. Even using the SORT_FLAG_CASE. The following worked for string comparision for me: array_multisort( array_map(strtolower, array_column($ipr_projects, 'Name')), SORT_ASC, $ipr_projects);
Working well PHP 7.4
47

Since your array elements are arrays themselves with string keys, your best bet is to define a custom comparison function. It's pretty quick and easy to do. Try this:

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

Produces the following:

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

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

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

)

2 Comments

Combining with some of the other comments here (uasort and inline anonymous functions), you get this one-liner: uasort( $inventory, function ($a, $b) { if ( $a==$b ) return 0; else return ($a > $b) ? -1 : 1; });
@AlanPorter usort seems more appropriate than uasort for sorting an array with sequential numeric keys. Ending up with an array where the first element is at index 1 and the second element is at index 0 is weird behavior and a sure trap for people who aren't familiar with the details of PHP's arrays; usort gives you the output you'd intuitively expect.
30

I ended on this:

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

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

Just call the function, passing the array and the name of the field of the second level array. Like:

sort_array_of_array($inventory, 'price');

1 Comment

It is unnecessary to preserve the $key in the temporary $sortarray. This answer can be reduced to one line without the custom function declarationa and the explicit direction constant usage. array_multisort(array_column($array, $subfield), $array);
21

You can use usort with an anonymous function, e.g.,

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

3 Comments

Versions PHP 5 >= 5.5.0, PHP 7 for those of you like me that really wanted this to work for them..
Remarkable that strnatcmp, meant for comparing strings, seems to work fine here. Apparently the "natural order" it implements includes sorting numerical strings numerically rather than lexically.
The spaceship operator performs the same kind of comparison without the iterated function calls.
17

From Sort an array of associative arrays by value of given key in PHP:

By using usort, we can sort an array in ascending and descending order. We just need to create a function and pass it as parameter in usort. As per below example used greater than for ascending order if we passed less than condition then it's sort in descending order.

Example:

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

Output:

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

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

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

4 Comments

This answer turned up in the low quality review queue, presumably because you don't provide any explanation of the code. If this code answers the question, consider adding adding some text explaining the code in your answer. This way, you are far more likely to get more upvotes — and help the questioner learn something new.
-1; the cmp function here is wrong. It's supposed to return "an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second" but instead returns true or false. It seems, remarkably, to nonetheless work - perhaps because the current implementation of usort and friends treats the "less than" and "equal to" cases identically - but don't count on it carrying on working in future PHP versions. If they try to make sorts be stable (i.e. not move around equal elements unnecessarily), this will break.
Also, usort would be more appropriate than uasort here, since uasort preserves the association between keys and values which is confusing and unexpected when deaing with a sequential numerical array. For instance, the indexes of $array above after calling uasort are 2, 0, and 1, in that order. Unless you for some reason want that, you'll probably be more comfortable using usort, which reindexes the array as well as reordering it.
in php7+: in the cmp function one should use the <=> 'spaceship' operator
13
$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");
}

outputs:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9

Comments

8
/**
 * Sorts an associative array by a specific key and direction.
 *
 * @param array $array The associative array to sort.
 * @param string $key The key to sort by.
 * @param string $direction The sort direction ("ASC" for ascending, "DESC" for descending).
 * @return array The sorted associative array.
 */
function sortAssociativeArrayByKey($array, $key, $direction) {
    usort($array, function ($a, $b) use ($key, $direction) {
        $comparison = $a[$key] <=> $b[$key];
        return ($direction === 'DESC') ? -$comparison : $comparison;
    });
    return $array;
}

Comments

7

As of PHP 7.4, you can use the arrow function:

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

Code (demo):

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

(Condensed) output:

Array
(
    [0] => Array ([type] => pork,  [price] => 5.43)
    [1] => Array ([type] => fruit, [price] => 3.5)
    [2] => Array ([type] => milk,  [price] => 2.9)
)

Comments

6

This was tested on 100,000 records:

Time in seconds (calculated by function microtime). Only for unique values on sorting key positions.

Solution of function of @Josh Davis:

Spent time: 1.58

My solution:

Spent time: 0.094

Solution:

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*
}

3 Comments

The requirement for unique sort keys is sort of a deal breaker, though. If you have unique sort values that can be keys, it begs the question: why not simply construct the array with those keys to begin with? In the OP's scenario, it is difficult to imagine that two items with the same price would be impossible. That in mind, using this solution would cause items from the array to mysteriously and silently disappear from the sorted result set.
@Chris Baker, you are right. This works only for unique values. But this solution works very fast, so speed was the reason of make and use it. At the moment may be it is not actual, need to test it with PHP 7.1.x.
array_column() can do what the foreach() is doing. If there are performance improvements by making key comparisons, it needs to be acknowledged that this answer doubles the memory consumption by populating the new array and as Chris Baker noted, if duplicate values are encountered in the identifying column, then whole rows will be lost. Also, if assigning keys that PHP will truncate (using a float type value as the key) then there may be side effects like lost data.
6

I use uasort like this:

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

2 Comments

Code-only answers are low-value on Stack Overflow because they do a poor job of educating/empowering the OP and thousands of future researchers. Furthermore, suggesting techniques that were already provided years earlier does NOT help researchers -- in fact, it wastes their research time because they end up reading more content but gain no new information.
No modern PHP applications should be returning a boolean type value in the custom callback; an integer is expected.
4

Try this:

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

2 Comments

Hi and welcome to stackoverflow, and thank you for answering. While this code might answer the question, can you consider adding some explanation for what the problem was you solved, and how you solved it? This will help future readers to understand your answer better and learn from it.
Please never duplicate the advice of previous posts (especially on the same page). This needlessly bloats Stack Overflow and wastes researchers' time.
3

This function is reusable:

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

It works well on string values by default, but you'll have to sub the callback for a number comparison function if all your values are numbers.

1 Comment

You call this usortarr but then call uasort instead of usort; perhaps a bit confusing. The latter is - in the case of a sequential array with numerical indexes, like the one exhibited in the question - probably what you actually want.
3

Many people are searching for a way to do this with Laravel and are ending up here. Also, some Laravel questions are getting closed due to duplicates to this question.

Hence, I shared an easy way to perform it with the Laravel collect() method.

$inventory = collect($inventory)->sortBy('price')->toArray();

For Descending order

$inventory = collect($inventory)->sortBy('price')->reverse()->toArray();

Or,

$inventory = collect($inventory)->('price')->reverse()->toArray();

Comments

2

Here is a method that I found long ago and cleaned up a bit. This works great, and can be quickly changed to accept objects as well.

/**
 * 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;

}

to change the method to sort objects simply change the following line:

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

To run the method simply do

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

2 Comments

This approach works, but is a fair bit less concise than either Josh Davis's answer (with array_multisort) or mine (with usort) and seems to offer no advantages over them in exchange.
I don't see myself ever having a need for such a script in a professional project. See also Case Insensitively Sort a Multidimensional PHP Array using array_multisort()
1

You might try to define your own comparison function and then use usort.

2 Comments

Yes. I'll do that if I can't find a solution. I'm pretty sure there are some weird parameters you can add to one of the sorts to accomplish this. Thanks for your thoughts though!
In comparison to the other answers posted here, this answer looks more like a "hint" that should be a comment under the question instead of an answer.
0
//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
    )

)

Comments

0

Complete Dynamic Function

I jumped here for associative array sorting and found this amazing function on sort. This function is very dynamic and sort in ascending and descending order with a specified key.

A simple function to sort an array by a specific key.

It maintains the index association.

<?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 Comment

if (count($array) > 0) { is unneeded before the foreach(). Is this script over-engineered? I'm not sure I'd use something like this versus a native array_multisort() or usort() call.
-1

If you need to sort an array of strings with different cases, this will change the sorting array values to lowercase.

$data = [
    [
        'name' => 'jack',
        'eyeColor' => 'green'
    ],
    [
        'name' => 'Amy',
        'eyeColor' => 'brown'
    ],
    [   
        'name' => 'Cody',
        'eyeColor' => 'blue'
    ] 
];
function toLowerCase($a) { return strtolower($a); }
$sortArray = array_map("toLowerCase",array_column($data, 'name'));
array_multisort($sortArray, SORT_ASC, $data);

1 Comment

-2
$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;


} 

2 Comments

While this code snippet may solve the question, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, as this reduces the readability of both the code and the explanations!
-2

This function works 100% on all major versions of PHP and it is tested with PHP5, PHP7, PHP8.

    function sort_my_array($array, $order_by, $order)
    {
        switch ($order) {
            case "asc":
                usort($array, function ($first, $second) use ($order_by) {
                    if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
                        return $first[$order_by] <=> $second[$order_by];
                    } else {
                        $array_cmp = strcmp($first[$order_by], $second[$order_by]);
                        return $array_cmp ;
                    }
                });
                break;
            case "desc":
                usort($certificates, function ($first, $second) use ($order_by) {
                    if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
                        return $first[$order_by] <=> $second[$order_by];
                    } else {
                        $array_cmp = strcmp($first[$order_by], $second[$order_by]);
                        return -$array_cmp ;
                    }
                });
                break;
            default:
                break;
        }
        return $array;
    }

1 Comment

Too much redundant code. If $order === 'desc', then multiply the returned value by -1. default doesn't need a break. An empty default is pointless. The version comparison will not change during script execution, so it makes no sense to check it on each iteration.
-7

try this:

asort($array_to_sort, SORT_NUMERIC);

for reference see this: http://php.net/manual/en/function.asort.php

see various sort flags here: http://www.php.net/manual/en/function.sort.php

2 Comments

this won't work for multidimensional arrays, but just helped me out for another problem, thanks :)
This can't be used to sort a list of dictionaries by a particular dictionary key, and hence doesn't answer the question posed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.