31

Suppose I have an array that mimics a database table. Each array element represents a row, and within each row is another array that contains the field names and values.

Array
(
    [0] => Array
        (
            [name] => 'Sony TV'
            [price] => 600.00
        )

    [1] => Array
        (
            [name] => 'LG TV'
            [price] => 350.00
        )

    [2] => Array
        (
            [name] => 'Samsung TV'
            [price] => 425.00
        )  
}

What I want to do is sort the rows (outer array elements) by price. Below is an example of what I want to achieve:

Array
(
    [0] => Array
        (
            [name] => 'LG TV'
            [price] => 350.00
        )

    [1] => Array
        (
            [name] => 'Samsung TV'
            [price] => 425.00
        )

    [2] => Array
        (
            [name] => 'Sony TV'
            [price] => 600.00
        )        
}

As you can see, I don't need to preserve the keys of the outer array.

jmcampbell
  • 378
  • 3
  • 16
Camsoft
  • 10,713
  • 18
  • 78
  • 119

8 Answers8

49

It is just a one liner

array_multisort( array_column($yourArray, "price"), SORT_ASC, $yourArray );

You can find it here as well: http://php.net/manual/en/function.array-multisort.php

search for "array_column" on that manual page.

UPDATE of Dec 1, 2020:

As @Tyler V. mentioned in his comment this syntax may cause errors in newer PHP 7 versions around "only variables can be passed by reference." To avoid those you need to change the one liner to a two liner unfortunately:

$col = array_column( $yourArray, "price" );
array_multisort( $col, SORT_ASC, $yourArray );
rf1234
  • 1,092
  • 9
  • 10
  • 1
    Great answer. IMHO, the top 2 answers here, while getting the job done, actually seem to be reinventing the wheel. HOWEVER, note that `array_column` is only available in PHP >= 5.5 – Ifedi Okonkwo May 21 '19 at 10:18
  • 2
    That's right Ifedi. People shouldn't use old PHP versions anyway. Even PHP 7.0 is deprecated by now ... Better to use PHP 7.2 or 7.3 – rf1234 May 24 '19 at 16:50
  • `SORT_ASC` is the default sorting direction and is omittable. – mickmackusa Dec 25 '19 at 12:53
  • 1
    @mickmackusa: you are right! But now you know where to put in SORT_DESC ... – rf1234 Jan 11 '20 at 10:04
  • 1
    In later versions of PHP 7 this will trigger errors around "only variables can be passed by reference." To solve this, move the output of array_column() to a variable and pass that variable to array_multisort() – Tyler V. Nov 30 '20 at 20:49
  • 1
    Should be the best answer, Thanks! ;) – flangofas Feb 04 '21 at 15:18
43

You need to use usort, a function that sorts arrays via a user defined function. Something like:

function cmp($a, $b)
{
    if ($a["price"] == $b["price"]) {
        return 0;
    }
    return ($a["price"] < $b["price"]) ? -1 : 1;
}

usort($yourArray,"cmp")
Dancrumb
  • 23,732
  • 7
  • 60
  • 127
15

You can use usort():

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

usort($array, 'sort');

Even better if you create a class like this to reuse the code:

class FieldSorter {
    public $field;

    function __construct($field) {
        $this->field = $field;
    }

    function cmp($a, $b) {
        if ($a[$this->field] == $b[$this->field]) return 0;
        return ($a[$this->field] > $b[$this->field]) ? 1 : -1;
    }
}

$sorter = new FieldSorter('price');    
usort($array, array($sorter, "cmp"));

This way, you can easily sort by other fields.

And although you said the the keys of the outer array don't have to be preserved you can easily achieve this by using uasort() instead of usort.

Felix Kling
  • 705,106
  • 160
  • 1,004
  • 1,072
9

This is basically the same as the accepted answer, but a couple of new features have been added to PHP over the years to make it more convenient to use usort for this.

$column = 'price';
usort($table, function($a, $b) use ($column) {
    return $a[$column] <=> $b[$column];
});

You can now use an anonymous function for the comparison callback (as of PHP 5.3), and PHP 7 introduced the combined comparison operator (<=>), which allows you to reduce the comparison logic

if ($a['price'] == $b['price']) return 0;
return ($a['price'] > $b['price']) ? 1 : -1;

to a single expression

return $a[$column] <=> $b[$column];
Don't Panic
  • 37,589
  • 9
  • 55
  • 71
1

This question is a bit old, but will leave the answer here for future.

From php.net-Multisort function we can use the code below;

    $data= [['volume' => 67, 'edition' => 2],['volume' => 85, 'edition' => 6],...];
foreach ($data as $key => $row) {
    $volume[$key]  = $row['volume'];
    $edition[$key] = $row['edition'];
}
array_multisort($volume, SORT_DESC, $edition, SORT_ASC, $data);

The above is for a static sorting of data where you manually change sort columns.

For more dynamic and robust example consider below;

Assume I have the data below;

$data   = [[1, 'Amanda', 'Wright', 'awright0@usnews.com', 'Female', '135.114.57.89', 31237],
           [2, 'Theresa', 'Larson', 'tlarson1@51.la', 'Female', '207.108.96.210', 91011],
           [3, 'Walter', 'Kennedy', 'wkennedy2@baidu.com', 'Male', '199.147.223.56', 50114],
           [4, 'Andrea', 'Richards', 'arichards3@google.nl', 'Female', '230.195.124.95', 76489],
           [5, 'Carol', 'Jones', 'cjones4@elegantthemes.com', 'Female', '250.197.111.90', 56501],
           [6, 'Alice', 'Freeman', 'afreeman5@elegantthemes.com', 'Female', '52.195.252.131', 77170],
           [7, 'Gerald', 'Fisher', 'gfisher6@slashdot.org', 'Male', '81.2.22.62', 75625],....]

If we need to sort the above array data out of the box, then we can set the sort orders in an array using the syntax;

 $qTable[$index]=$sort_order;
E.g. $qTable=[1=>'asc',4=>'desc',3=>'asc'];

This means sort column 1 ASC, column 4 DESC then column 3 ASC. We can then using the function below to sort our multidimension database data;

   function sortMulti($data, $orders)
    {
        $args = [];
        foreach ($data as $key => $row) {
            foreach ($orders as $index => $order) {
                if (!isset($row[$index])) continue; //Ignore if column does'nt exist
                $args[$index]['d'][$key] = $row[$index]; //Get all values within the column
                $args[$index]['o']       = 'desc' == strtolower($order) ? SORT_DESC : SORT_ASC; //Get the Sort order 'ASC' is the default
            }
        }
        $p = [];
//Below we need to organize our entries as arguments for array_multisort
        foreach ($args as $arg) {
            $p[] = $arg['d'];
            $p[] = $arg['o'];
//Below we need to check if column contains only numeric or not.
//If all values are numeric, then we use numeric sort flag, otherwise NATURAL
//Manipulate for more conditions supported
            $p[] = count($arg['d']) == count(array_filter($arg['d'], 'is_numeric')) ? SORT_NUMERIC : SORT_NATURAL;
        }
        $p[] = &$data; //Pass by reference
        call_user_func_array('array_multisort', $p); //Call Php's own multisort with parameters in required order.
        return $data; //Our final array sorted.
    }

Then we can use it as below;

$data=[[...],[...],...];
$order=[1=>'asc',4=>'desc',3=>'asc'];

$sorted=sortMulti($data,$order);

For key value array data E.g. $data=[['c1'=>1212,'c2'=>'mynames'],...]; Use the order as $order=['c1'=>'desc','c10'=>'asc'];

I tested the above with an array of 1000 records. Hope it helps someone.

McAngujo
  • 115
  • 2
  • 7
-1

You can use the usort function with a callback

http://www.php.net/manual/en/function.usort.php

Vals
  • 259
  • 2
  • 7
  • When compared to other answers, this link-only answer looks rather undercooked and unlikely to be helpful to researchers. – mickmackusa Dec 30 '19 at 10:38
-1

You can create a function yourself like the one below

private function orderArrayBycolumn($array, $column){

    $newArray = [];
    foreach ($array as $key => $value) {
        $newArray[$value[$column]] = $value;
    }

    $array = [];

    ksort($newArray);

    foreach ($newArray as $key => $value) {
        $array[] = $value;
    }      

    return $array;
}
  • "Looping, then sorting, then looping" is not an attractive or efficient suggestion. Why did you bother to re-post user2182349's answer? – mickmackusa Dec 30 '19 at 10:35
-1

I just want to make a couple additions...

@rf1234's answer is probably what I would go for (because I read somewhere that array_multisort() outperforms usort(), but I haven't personally benchmarked them), but the sorting direction does not need to be declared because the default direction is ASC.

Code: (Demo)

$column = 'price';
array_multisort(array_column($array, $column), $array);
var_export($array);

@Don'tPanic's answer using usort() with the spaceship operator is also attractive. From PHP7.4, the syntax can be reduced and the use() expression can be removed by using arrow function syntax. This technique allows $column to freely enter the function scope without use().

Code: (Demo)

$column = 'price';
usort($array, fn($a, $b) => $a[$column] <=> $b[$column]);
var_export($array);

I recommend either of these correct, efficient, direct solutions.

mickmackusa
  • 33,121
  • 11
  • 58
  • 86