4

I'm trying to understand how the items from my array are passed to my value compare function when using usort(). A printout of the values of $x and $y for each iteration follows:

Iteration 1:

// $x
array(2) { ["k1"]=> int(21) ["k2"]=> string(1) "e" }

// $y
array(2) { ["k1"]=> int(920) ["k2"]=> string(1) "z" }

Iteration 2:

// $x
array(2) { ["k1"]=> int(842) ["k2"]=> string(1) "t" }

// $y
array(2) { ["k1"]=> int(21) ["k2"]=> string(1) "e" } 

Iteration 3:

// $x
array(2) { ["k1"]=> int(920) ["k2"]=> string(1) "z" } 

// $y
array(2) { ["k1"]=> int(21) ["k2"]=> string(1) "e" } 

Iteration 4:

// $x
array(2) { ["k1"]=> int(842) ["k2"]=> string(1) "t" } 

// $y
array(2) { ["k1"]=> int(920) ["k2"]=> string(1) "z" } 

My data:

$data = array(
    array( 'k1' => 920, 'k2' => 'z' ),
    array( 'k1' => 21, 'k2' => 'e' ),
    array( 'k1' => 842, 'k2' => 't' )
);

My custom function:

function value_compare_func( $x, $y ) {
    if ( $x['k1'] > $y['k1'] ) {
        return true;
    } elseif ( $x['k1'] < $y['k1'] ) {
        return false;
    } else {
        return 0;
    }
}

Sort the array:

usort( $data, 'value_compare_function' );

For the first iteration, $x['k1'] is $data[1]['k1'] and $y['k1'] is $data[0][k1]. Why aren't the items from my $data array passed to value_compare_func() in order? For example, I would have expected $x['k1'] to be $data[0]['k1'] and $y['k1'] to be $data[1]['k1'] for the first iteration, but this isn't the case.

8
  • 1
    your compare function is wrong. You should return -1 not false on $x < $y , false is considered 0 in PHP Commented Nov 21, 2015 at 23:40
  • Look here for reference: github.com/php/php-src/blob/… , the function that php uses is zend_hash_sort_ex in this file Commented Nov 21, 2015 at 23:46
  • I see the manual says my value compare function should return int values (1, -1 and 0) but using false in place of -1 and true in place of 1 results in a sorted array with the exact same sequence of values. Commented Nov 22, 2015 at 0:42
  • Also, the values I dump during each iteration don't change either. Commented Nov 22, 2015 at 0:47
  • Have you checked out the php sourcecode I sent? Its not easy but the quicksort implementation can be found there. Anyways as comparefunc should do nothing else with the data than compare I suggest you dont write any orderdepending code in there.. if you need certain access order, youll have to implement your own sort function, but itll be at cost of performance Commented Nov 22, 2015 at 0:50

2 Answers 2

2

The answer to how these items are passed to the comparison function would require understanding the quicksort algorithm. The gist of it is that some element in the array is assigned as a pivot (it could be any element at all really, but in efficient implementation's it's typically the median element) and then comparison on either side of the pivot is done to sort the array.

This is the underlying implementation of usort in PHP, basically.

So trying to observe the order in which elements are passed to the comparison function is relatively useless. The order is completely unimportant. What's important is that you can rest assured they will always be sorted correctly according to what your callback function returns.

The important thing to note here is that the manual explicitly warns against returning values from the callback that are not integers for a very specific reason (they will be cast to integers) and in your example here you are returning a boolean false from the callback which will become 0 when cast to an integer. Since 0 indicates both values are equal you will not and should not expect a properly sorted array here.

Always make sure to return an integer value of 1 when $a > $b, an integer value of -1, when $a < $b and an integer value of 0 when $a == $b from your callback.

Sign up to request clarification or add additional context in comments.

5 Comments

The use of false in place of -1 and true in place of 1 as return values doesn't seem to cause the array to be sorted any differently to how the array is sorted when I return int values. Larry Ullman in his book PHP Advanced & OOP 3rd ed uses bool return values. Could bool values be acceptable, just not mentioned in the manual? Regardless of this, it was the first part of your answer I was interested in (how the items are passed to the comparison function).
Maybe same result but check iterations with updated comp func
The values I dump during each iteration don't change either.
It absolutely does matter whether you return a boolean false or a -1 from the callback. It is only by mere coincidence in your particular example here that the sorted result didn't change, but returning false is definitely wrong, because that's not what is expected from the callback. You are simply discovering undefined behavior. My suggestion is not to rely it.
@henrywright They may not change for you, but that doesn't mean anything, because once again undefined behavior See how the order of comparison means nothing whatsoever in terms of the resulting sort The point is you really shouldn't concern yourself so much with what order they are compared in, which is undefined, as you should with the well defined behavior that is expected of the callback's return values in order to get a valid result every time.
0

The compare function should return a numerical value. Since you return booleans, the comparison becomes unpredictable as false == 0 and so the distinction between the two is lost in sorting.

You should change your code as follows:

function value_compare_func( $x, $y ) {
    if ( $x['k1'] > $y['k1'] ) {
        return 1;
     } elseif ( $x['k1'] < $y['k1'] ) {
         return -1;
     } else {
         return 0;
     }
 }

... and the results will be more logical.

Comments

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.