89

I've an array like this

Array (
 [0] => Array( "destination" => "Sydney",
               "airlines" => "airline_1",
               "one_way_fare" => 100,
               "return_fare => 300
       ),
 [2] => Array( "destination" => "Sydney",
               "airlines" => "airline_2",
               "one_way_fare" => 150,
               "return_fare => 350
       ),
 [3] => Array( "destination" => "Sydney",
               "airlines" => "airline_3",
               "one_way_fare" => 180,
               "return_fare => 380
       )
)

How can i sort the value by return_fare asc , one_way_fare asc ?

I tried array_multisort() but i ended up getting mixed up data..

asort only works for one dimensional array, i need to sort by two values or more, how can i achieve this like in SQL, order by field1 asc,field2 asc ?

0

6 Answers 6

154

array_multisort() is the correct function, you must have messed up somehow:

// Obtain a list of columns
foreach ($data as $key => $row) {
    $return_fare[$key]  = $row['return_fare'];
    $one_way_fare[$key] = $row['one_way_fare'];
}

// Sort the data with volume descending, edition ascending
array_multisort($data, $return_fare, SORT_ASC, $one_way_fare, SORT_ASC);

If you take a look at the comments at PHP's manual page for array_multisort(), you can find a very helpful array_orderby() function which allows you to shorten the above to just this:

$sorted = array_orderby($data, 'return_fare', SORT_ASC, 'one_way_fare', SORT_ASC);

To avoid the looping use array_column() (as of PHP 5.5.0):

array_multisort(array_column($data, 'return_fare'),  SORT_ASC,
                array_column($data, 'one_way_fare'), SORT_ASC,
                $data);
Sign up to request clarification or add additional context in comments.

4 Comments

thanks..! i think i did it exactly the same u advised. can't find the array_orderby() function though on manual page comments
Many Thanks. The array_orderby worked for me. however i had to copy the function for the PHP website.. :)
reduced my original multiline solution to one line - perfect - thanks
50

In addition to array_multisort(), which requires you to build column-arrays first, there is also usort() which doesn't require such a thing.

usort($data, function($a, $b) { 
    $rdiff = $a['return_fare'] - $b['return_fare'];
    if ($rdiff) return $rdiff; 
    return $a['one_way_fare'] - $b['one_way_fare']; 
}); // anonymous function requires PHP 5.3 - use "normal" function earlier

4 Comments

Or more shortly, return $a['return_fare'] - $b['return_fare'] ?: a['one_way_fare'] - $b['one_way_fare']; Everything feels more natural than array_multisort()
i stumbled upon this one, i think it should be said that array_multisort seems to be significantly faster than usort - you can find an example here sandbox.onlinephpfunctions.com/code/…
Could very well be the case now (back in 2011 there was no arrray_column and so you had to build the arrays yourself which slowed things down considerably).
A foreach loop that generates two arrays is very likely to outperform two separate array_column() calls ...if you are worried about micro-optimization. The benefit of two array_column() calls is code brevity and the avoidance of making two variable declarations.
17

Another example using the spaceship operator.

usort($data, function($a, $b) { 
    return $a['return_fare'] <=> $b['return_fare'] ?: $a['one_way_fare'] <=> $b['one_way_fare'];
});

5 Comments

This can be done with a single spaceship operator comparison.
And yet you don't tell us how @mickmackusa?
My answer on the duplicate page demonstrates how to do this. @codemonkey
Here are some more if you like: stackoverflow.com/… For the record, if you are making function calls in the comparisons, then I recommend using multiple spaceships, but if there are no function calls, then it is better to use one spaceship. My old answers might not toe this line.
Ah ha. I'd considered concatenation but knew that wasn't safe - arrays hadn't occured to me, thanks.
11

Or you can use uasort as follows

uasort($arr, function($a,$b){
    $c = $a['return_fare'] - $b['return_fare'];
    $c .= $a['one_way_fare'] - $b['one_way_fare'];
    return $c;
});

Fiddle

3 Comments

This is not working when sorting with decimal values
This has been an awesome tip: I was trying to do a double sort (group job IDs then order product IDs within the groups) and this did the trick perfectly (I was looking for a MySQL multi column sort solution, I had tried array_multisort and it was breaking other ordered sections in the groups - this is the only solution that has allowed me to get the desired order in a way I could read and understand, a very important thing when it comes to refactoring further down the line)
I would never use the concatenated string formed from two difference values. Even in the cases where it works, there are cleaner techniques available.
9

I'll answer this in a way that can be generalized, no matter how many items you wish to sort on!

Sorting on return_fare then one_way_fare:

usort($data, function($a, $b) {
   if ($a['return_fare'] != $b['return_fare']) {
      return $a['return_fare'] <=> $b['return_fare'];
   }

   return $a['one_way_fare'] <=> $b['one_way_fare'];
});

Sorting on return_fare, then one_way_fare, then destination:

usort($data, function($a, $b) {
   if ($a['return_fare'] != $b['return_fare']) {
      return $a['return_fare'] <=> $b['return_fare'];
   }

   if ($a['one_way_fare'] != $b['one_way_fare']) {
      return $a['one_way_fare'] <=> $b['one_way_fare'];
   }

   return strnatcasecmp($a['destination'], $b['destination']);
});

Sorting on just return_fare:

usort($data, function($a, $b) {
   return $a['return_fare'] <=> $b['return_fare'];
});

Note: you don't have to use an anonymous function with usort!

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

usort($data, 'cmp');


// Use a function inside a class:
class MyClass {
   public static function compare($a, $b) {
      return $a['return_fare'] <=> $b['return_fare'];
   }
}

usort($data, ['MyClass', 'compare']);

You can also chain these using the Elvis Operator (?:):

usort($data, function($a, $b) {
      return $a['return_fare'] <=> $b['return_fare'] ?:
             $a['one_way_fare'] <=> $b['one_way_fare'] ?:
             strnatcasecmp($a['destination'], $b['destination']);
});

This last example used the Spaceship Operator (<=>) and the Elvis Operator (?:). Isn't programming great?

Comments

-2

Ohh, i managed to solve my own question again....

function array_multi_sort($array, $on1,$on2, $order=SORT_ASC) 
{

    foreach($array as $key=>$value){
        $one_way_fares[$key] = $value[$on2];
        $return_fares[$key] = $value[$on1];
    }

    array_multisort($return_fares,$order,$one_way_fares,$order,$array);
}

The thing is i missed out the last parameter $array on array_multisort($return_fares,$order,$one_way_fares,$order,$array);

earlier!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.