Can anyone explain how uksort() in PHP works? - php

I have read about uksort in the PHP manual but it is very difficult to understand.
Can any one help me out?
Here is an example:
<?php
function my_sort($x, $y)
{
if ($x == $y) return 0;
return ($x > $y) ? -1 : 1;
}
$people = array(
"10" => "javascript",
"20" => "php", "60" => "vbscript",
"40" => "jsp");
uksort($people, "my_sort");
print_r($people);
?>
What is happening here?

As said in the manual, your function (my_sort in this case) should return:
a negative integer (in this case -1) if you consider $a to be less than $b
a positive integer if you consider $a to be greater than $b
0 if you consider them to be the same.
As you may have guessed, uksort will use your comparison function to see in which order the elements should be in the sorted array. It will call your function multiple times, every time with two keys. You compare those to keys to each other and give your result back.
The idea is that you can program your own comparison function which does something non-trivial, for example if you want a certain key to always be first. Your trivial example can use the regular krsort instead.

Related

usort and compare function with arguments PHP

I have question
My code works but i dont understand what is $x[1] and $y[1] in function
I tryed $x[0], $x[1], $x[2], $y[0], $y[1], $y[2] and dont get the logical output ? Where i am wrong to understand ? Please if someone can help me ?
<?php
$products = array( array('TIR', 'Tires', 100),
array('OIL', 'Oil', 10),
array ('SPK', 'Spark Plugs', 4));
//print_r ($products); echo '<br />';
function compare($x, $y) {
if ($x[1] == $y[1]) {
return 0;
} else if ($x[1]<$y[1]) {
return -1;
} else {
return 1;
}
}
usort ($products, 'compare');
echo compare('Tires', 'Tires' );
echo compare('Oil', 'Spark Plugs' );
echo compare('Spark Plugs', 'Oil' );
echo compare('Tires', 'Tires');
echo '<br />';
Output is for this code for echo 1, 2, 3, 4:
0
-1
1
0
When you call the compare($x, $y) function, you are passing the strings as the parameters. These string are treated as arrays with 0-based indexing.
So, when echo compare('Tires', 'Tires' ); is executed, these two strings are passed and according to compare function, the character at index 1(indexing starts at 0) i.e. 2nd character is compared.
So, for this ```echo compare('Tires', 'Tires' );```, the compared characters are 'i' and 'i' which are equal and hence 0 is returned.
So, for this echo compare('Oil', 'Spark Plugs' );, the compared characters are 'i' and 'p'. 'i' is less than p and hence -1 is returned. To decide which character is lower than the other, lookup ASCII codes.
And so on for other function calls. Let me know, if you still have any doubt.
This I have explained for just the independent echo compare('Oil', 'Spark Plugs' ); line not for usort function.
UPDATE For the usort function
Let me first explain the way a comparator functions works. Whenever two parameters are passed to the compare function, it returns true or false and this is used to determine whether you need to swap those values or not.
In the earlier case, echo compare('Tirez', 'Tires' );
$x = Tires, and
$y = Tirez
You compare $x[1] and $y[1], particularly the character at index 1. But what if in the case of these strings, you just do $x < &y, the strings are compared automatically character-by-character according to ASCII codes for English alphabets and the result is returned on the first position, the characters do not match.
i.e. if you want to compare if one string is lexicographically smaller than the other string then you can use the below comparator function.
function compare($x, $y) {
if ($x == $y) {
return 0;
} else if ($x < $y) {
return -1;
} else {
return 1;
}
}
The output will be 1, since while comparing character-by-character 'z' > 's'.
So, when a complete array is passed to compare function, the first two elements are passed. Here the array $products is a 2D array (an array of arrays), so the first two arrays are passed
i.e. $x = array('TIR', 'Tires', 100), and
      $y = array('OIL', 'Oil', 10)
So, it depends on your requirement. For example, if you want to sort by index 0 of any array of $products i.e. 'TIR', 'OIL', 'SPK' then change the comparator function to $x[0] and $y[0].
I hope you are able to understand now :).

What is the reason of returning -1 instead of lets say 0 at the end of this function's code?

I am talking about the second "return -1;" on the 12th line of the code. This gets reached only if two sets of numbers are exactly the same, like when comparing '192.167.11' to '192.167.11'. I will also add that using range(0,2) would be a better option for this piece of code (range(0,3) produces errors if two elements happen to be the same; I did not change that as this is the original code example from PHP Array Exercise #21 from w3resource.com).
<?php
function sort_subnets($x, $y){
$x_arr = explode('.', $x);
$y_arr = explode('.', $y);
foreach (range(0, 3) as $i) {
if ($x_arr[$i] < $y_arr[$i]) {
return -1;
} elseif ($x_arr[$i] > $y_arr[$i]) {
return 1;
}
}
return -1;
}
$subnet_list =
array('192.169.12',
'192.167.11',
'192.169.14',
'192.168.13',
'192.167.12',
'122.169.15',
'192.167.16'
);
usort($subnet_list, 'sort_subnets');
print_r($subnet_list);
?>
Returning "-1" would move the second element (the same as the first in the current $x and $y pair) towards the higher index of the array (down the array). Why not return "0" and keep everything as is if the two elements are exactly the same? Is there any reason for returning the "-1" maybe based on how the usort() works (or any other factor of this)?
Thanks.
EDIT:
I think that this is Insertion Sort (array size 6-15 elements; normally it would be Quicksort).
If the two elements are the same, there's no difference between swapping the order and keeping the order the same. So it doesn't make a difference what it returns in that case.
You're right that 0 is more appropriate. This would be more important if usort were "stable". But the documentation says
Note:
If two members compare as equal, their relative order in the sorted array is undefined.
To illustrate the excellent point of #Don'tPanic:
<?php
function sort_subnets($x, $y){
$x_arr = explode('.', $x);
$y_arr = explode('.', $y);
return $x_arr <=> $y_arr;
}
$subnet_list =
array('192.169.12',
'192.167.11',
'192.169.14',
'192.168.13',
'192.167.12',
'122.169.15',
'192.167.16'
);
usort($subnet_list, 'sort_subnets');
print_r($subnet_list);
See live code
Note the use of the "spaceship" operator, namely <=> which offers a conciseness that spares one from having to write code like the following in a function:
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
Lastly, note that the user-defined callback for usort() makes use of ternary logic because sometimes as in the case of sorting bivalent logic is insufficient. Yet, usort() itself utilizes two-part logic, returning TRUE on success and FALSE on failure.

How to view the steps of the usort() in PHP?

This is the Example #1 from the php.net usort() page:
<?php
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$a = array(3, 2, 5, 6, 1);
usort($a, "cmp");
foreach ($a as $key => $value) {
echo "$key: $value\n";
}
?>
The usort function takes the values within the array as pairs ($a-$b; so this is - 3-2, 2-5, 5-6, 6-1) and moves the $b value depending on whether the cmp() function returns -1, 0 or 1. If it is -1 the $b gets moved down (within a current pair), if it is 0 it stays in the same place and if it is 1 it gets moved up. This is how this is suppose to be working based on the top comment from the php.net manual usort() page.
Is there any way to see how this works step by step (the sorting process)? Am I able to see it or is it only possible to see the final result after the sorting is done? I want to understand fully how this process works.
Using some debug output in your comparison function you can only see the comparisons that PHP does, but can't see intermediate states of the array.
But the algorithm used in usort is well known - it's QuickSort ( Which sort algorithms does PHP's usort apply? ).
You can see its vizualisation at http://www.algomation.com/algorithm/quick-sort-visualization (or just google "quicksort algorithm visualization")

sort array by length and then alphabetically

I'm trying to make a way to sort words first by length, then alphabetically.
// from
$array = ["dog", "cat", "mouse", "elephant", "apple"];
// to
$array = ["cat", "dog", "apple", "mouse", "elephant"];
I've seen this answer, but it's in Java, and this answer, but it only deals with the sorting by length. I've tried sorting by length, using the code provided in the answer, and then sorting alphabetically, but then it sorts only alphabetically.
How can I sort it first by length, and then alphabetically?
You can put both of the conditions into a usort comparison function.
usort($array, function($a, $b) {
return strlen($a) - strlen($b) ?: strcmp($a, $b);
});
The general strategy for sorting by multiple conditions is to write comparison expressions for each of the conditions that returns the appropriate return type of the comparison function (an integer, positive, negative, or zero depending on the result of the comparison), and evaluate them in order of your desired sort order, e.g. first length, then alphabetical.
If an expression evaluates to zero, then the two items are equal in terms of that comparison, and the next expression should be evaluated. If not, then the value of that expression can be returned as the value of the comparison function.
The other answer here appears to be implying that this comparison function does not return an integer greater than, less than, or equal to zero. It does.
Note: I didn`t post my answer early,because #Don't Panic faster then me. However,I want to add some explanation to his answer ( hope, it will be useful for more understanding).
usort($array, function($a, $b) {
return strlen($a) - strlen($b) ?: strcmp($a, $b);
});
Ok. Function usort waits from custom comparison function next (from 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.
Ok, rewrite #Don't Panic code to this view (accoding the condition above):
usort($array, function($a, $b) {
// SORT_ORDER_CONDITION_#1
// equals -> going to next by order sort-condition
// in our case "sorting alphabetically"
if (strlen($a) == strlen($b)){
// SORT_ORDER_CONDITION_#2
if (strcmp($a,$b)==0) // equals - last sort-condition? Return 0 ( in our case - yes)
return 0; //
return (strcmp($a,$b)) ? -1 : 1;
}else{
return (strlen($a) < strlen ($b) ) ? - 1 : 1;
}
});
"Common sort strategy" (abstract) with multi sort-conditions in order like (CON_1,CON_2... CON_N) :
usort($array, function(ITEM_1, ITEM_2) {
// SORT_ORDER_CONDITION_#1
if (COMPARING_1_EQUALS){
// SORT_ORDER_CONDITION_#2
if (COMPARING_2_EQUALS){ // If last con - return 0, else - going "deeper" ( to next in order)
//...
// SORT_ORDER_CONDITION_#N
if (COMPARING_N_EQUALS) // last -> equals -> return 0;
return 0;
return ( COMPARING_N_NOT_EQUALS) ? -1 : 1;
//...
}
return ( COMPARING_2_NOT_EQUALS) ? -1 : 1;
}else{
return ( COMPARING_1_NOT_EQUALS ) ? - 1 : 1;
}
});
In practise (from my exp), it's sorting unordered multidimensional-array by several conditions. You can use usort like above.
This is not as short as other methods, but I would argue that it's clearer, and can be easily extended to cover other use cases:
$f = function ($s1, $s2) {
$n = strlen($s1) <=> strlen($s2);
if ($n != 0) {
return $n;
}
return $s1 <=> $s2;
};
usort($array, $f);

Difference between == and === WRT arrays in php?

I'm reading about php and it says,
== is Equality such that $a == $b is true if $a and $b have the same elements.
=== is Identity such that $a === $b is true if $a and $b have the same elements, with the same types, in the same order.
So, I thought I'd try and see the difference myself and wrote with this little script:
$a = array(1, 2, 3);
$b = array(2, 3, 1);
if ($a==$b) {echo "yeehaw!";} else {echo "nope";}
if ($a===$b) {echo "yup";} else {echo "nope";}
My thought was that the same order wasn't required for two arrays to be equal. However, when I ran this, I got "nope" and "nope".
What is the difference?
The arrays you've provided have the same set of values, but different key-value-pairs.
Try the following use case instead (same key-value-pairs in different order):
$a = array(0=>1, 1=>2, 2=>3);
$b = array(1=>2, 2=>3, 0=>1);
... and the following use case (different data types):
$a = array(1, 2, 3);
$b = array('1', '2', '3');
The documentation[PHP.net] says:
== TRUE if $a and $b have the same key/value pairs.
=== TRUE if $a and $b have the same key/value pairs in the same order and of the same types.
Since your two arrays are not in the same order1, they don't have the same key-value pairs.
var_dump($a);
array(3) {
[0]=> int(1)
[1]=> int(2)
[2]=> int(3)
}
var_dump($b);
array(3) {
[0]=> int(2)
[1]=> int(3)
[2]=> int(1)
}
1 With regards to their construction via array(), which will index the arguments starting with 0.
My thought was that the same order wasn't required for two arrays to be equal.
To make it clear what the documentation meant by same key/value pairs, let's take a look at the actual array contents:
$a = array(
0 => 1,
1 => 2,
2 => 3,
);
$b = array(
0 => 2,
1 => 3,
2 => 1,
);
Clearly, the pairs are different.
So what about that "same order"?
To illustrate that, let's create $b a little different:
$b => array(
2 => 3,
1 => 2,
0 => 1,
);
The == equality will be satisfied because the pairs are now the same. However, because arrays in PHP are ordered maps, a difference in pair order causes the === equality to fail.
Two arrays are considered identical === if:
number of elements is the same
all data types are the same
all elements are in the same order
each array has the same key-value pairs
What is the difference?
The difference between two arrays can mean different things, so this question is normally best answered by using the kind of difference function for arrays that match your expectations.
In your case, equality is (probably) satisfied by the array_diff() function:
!array_diff($a, $b) && !array_diff($b, $a);
If you say no, that's not what I'm looking for, please see the duplicate question "PHP - Check if two arrays are equal" I also left an extended answer there that shows the other possible differences and how to test for those as you're concerned about comparing values and not element (which are key/value pairs).
1st one fails because the elements are different. 2nd one fails because elements are different although type is same. (both should same)

Categories