Can't understand the usort algorithm, how the user defined function gets the arguments

Hi,
Kindly see the program on the following link:
usort example

<?php function list_cmp($a, $b)
{
   global $order;
   foreach($order as $key => $value)
   {
      if($a==$value)
     {
        return 0;
        break;
    }
    if($b==$value)
   {
      return 1;
      break;
   }
}
}
$order[0] = 1; $order[1] = 3;  $order[2] = 4;  $order[3] = 2;  $array[0] = 2; $array[1] = 1;  $array[2] = 3;  $array[3] = 4;  $array[4] = 2;  $array[5] = 1;  $array[6] = 2;  

usort($array, "list_cmp");  print_r($array);  ?>

We are not passing values of $a and $b. How list_cmp(…) will get their values?

Zulfi.

all array sorting functions are passed two parameters under the hood; you notice you dont have to specify to walk through the entire array either.

PHP takes care of the arrangement of ‘walk the array; for each pair of elements, compare and sort.’ your function then must take 2 parameters (the two individual items being sorted at that given moment), and return a value that PHP can use to do the actual sorting.

Note that most comparison functions for sorting return what’s called a “Spaceship Operator Value” - namely, -1,0, or 1:

if -1, A goes before B.
if 0, A and B are of equal value, and are left unsorted relative to each other.
if 1, B goes before A.

1 Like

You should imagine usort does something like this:

function usort($array, $callback) {
    // Get pairs of elements from $array and loop over them
    // Actual implementation of createPairsToSort is out of scope
    foreach ([$firstElement, $secondElement] = createPairsToSort($array)) {
        // Call the user defined function here to see how we should sort
        // these two elements
        $result = $callback($firstElement, $secondElement);

        if ($result < 1) {
            // update $array so $firstElement comes before secondElement
        } else if ($result === 0) {
           // keep elements in order, leave $array alone
        } else if ($result > 1) {
           // update $array so $firstElement comes after secondElement
        }
    }
}
1 Like

For those who don’t know, the spaceship operator is <=>.

So in PHP 8 you can also sort like this:

usort($array, fn ($first, second) => $first <=> $second);
1 Like

[quasi-off-topic]
I know it’s called a spaceship operator for the shape. But i’ve always seen it more like a seesaw. The pivot’s at 0, and it’s A (-1) and B (1) sitting on a seesaw, seeing which way it tips.
[/off-topic]

2 Likes

Hi,
What is $value and what is $key in the following code:

<?php function list_cmp($a, $b)
{
   global $order;
   foreach($order as $key => $value)
   {
      if($a==$value)
     {
        return 0;
        break;
    }
    if($b==$value)
   {
      return 1;
      break;
   }
}
}
$order[0] = 1; $order[1] = 3;  $order[2] = 4;  $order[3] = 2;  $array[0] = 2; $array[1] = 1;  $array[2] = 3;  $array[3] = 4;  $array[4] = 2;  $array[5] = 1;  $array[6] = 2;  

usort($array, "list_cmp");  print_r($array);  ?>

Try echoing $key and see what it is. (probably want to add a line break afterward)
Then try echoing $value.
What is the relationship between the two?

1 Like

Why do you have $order involved in list_cmp() at all? To my view, the whole point of usort() is that you’ve passed $order into it in the first place, so it’s already sorting that array. You seem to be adding another “layer” of accessing the array. You’re also bringing in a global there, which is my pet hate. In your code, if you want to sort a different array but use the same list_cmp, you’ll have to have another function to use a different global array. To me, any function should be able to work with the parameters it’s passed in, and not use globals at all.

Agreed, the global is weird.

I’d write it like this:

<?php

$order = [1, 3, 4, 2];
$array = [2, 1, 3, 4, 2, 1, 2];

usort(
    $array,
    function ($a, $b) use ($order) {
        foreach($order as $key => $value) {
            if ($a == $value) {
                return 0;
            }
            if ($b == $value) {
                return 1;
            }
        }
    }
);

print_r($array);
1 Like

code sanity screams that you need a fall-through return.

Also shouldnt the 0 be a -1?

Not my code, just my formatting :slight_smile:

But yes, I’m missing a return -1 case as well.

Hi my friends-

I tried to compile the program given in the post#1 and repeated in post#6 but I am getting the following error:

2021/10/31 22:51:28 [error] 1848#1848: *1 FastCGI sent in stderr: “PHP message: PHP Parse error: syntax error, unexpected ‘$b’ (T_VARIABLE), expecting ‘,’ or ‘)’ in /var/www/html/w3_usortprog.php on line 1” while reading response header from upstream, client: 127.0.0.1, server: _, request: “GET /w3_usortprog.php HTTP/1.1”, upstream: “fastcgi://unix:/var/run/php/php7.2-fpm.sock:”, host: “localhost”

Somebody please guide me.

Zulfi.

I’m going to guess that you’ve missed the comma in ($a, $b) as the error is in line 1 and it states that it was expecting either , or ) in the message.

1 Like

Hi my friend-
Thanks for your response. I have put the comma.
I have attached the image of my code:

KIndly help me to solve this probrlem.

Zulfi.

If I paste your code from post #6 (I can’t post the latest one because you’ve put it as an image, so I presume they’re the same) and run it in 3v4l.org PHP tester, there are no error messages and it outputs the array in what I presume is the correct order.

In some cases now I’d be looking for control characters or similar that might have crept in to your file, maybe delete that top line and type it in again and see if that helps.

1 Like

Hi,
Thanks.
I found some extra characters on every line. I transferred my program from word to libreoffice.

I am again understanding the program according to the output.

Zulfi.

MS-Word is never a good code editor, just like it’s not a good HTML editor. I’m using Notepad++ for PHP editing, though there are many more capable things available.

1 Like

Hi my friends,

Sorry I am still struggling with usort. After:
global $order;
I added following two lines:

echo 'value of dollar_a is $a=' . "$a" . "<BR>";  //modified
   echo 'value of dollar_a is $b=' . "$b" . "<BR>";  //modified

I got the following output:

value of dollar_a is $a=2
value of dollar_a is $b=1
value of dollar_a is $a=2
value of dollar_a is $b=3
value of dollar_a is $a=1
value of dollar_a is $b=3
value of dollar_a is $a=2
value of dollar_a is $b=4
value of dollar_a is $a=3
value of dollar_a is $b=4
value of dollar_a is $a=2
value of dollar_a is $b=2
value of dollar_a is $a=2
value of dollar_a is $b=1
value of dollar_a is $a=2
value of dollar_a is $b=1
value of dollar_a is $a=4
value of dollar_a is $b=1
value of dollar_a is $a=3
value of dollar_a is $b=1
value of dollar_a is $a=1
value of dollar_a is $b=1
value of dollar_a is $a=2
value of dollar_a is $b=2

It looks that $a & $b values correspond to the $array values. Initially $a=2 and $b = 1, i.e., first two values of $array. $Key i.e. index of $order is 0, hence, $value is 1. Now in the for loop we have :

if(2 == 1)

which is false, so we would return 0, where we would go? How the loop is continuing after return?
Somebody please guide me.
Zulfi.

Surely, it isn’t continuing? It’s just being called repeatedly by usort with different parameters. Why not add a few more echo statements so you can follow exactly what is happening? Add one after the end of the loop, and see if it displays, for example.

1 Like

Hi,

I would try what you said about printing values. But after ‘return’ we have ‘break’, why are we not getting unreachable code warning?

Zulfi.