Help with user defined sort function - php

function sort_searches($a, $b)
{
return
(
(isset($b['Class_ID']) && !isset($a['Class_ID']))
||
($b['Results'] && !$a['Results'])
||
(is_array($a['Results']) && !$a['Results'] && !is_array($b['Results']))
);
}
I'm using this function in usort(). The intended effect is that a list of searches will be sorted first by whether they have Class_IDs, and then second by results (with a non-empty array of results > results === false > results === empty array(). So a sorted set of searches would look like:
Class_ID with results
Class_ID with results === false
Class_ID with results === array()
No Class_ID with results
No Class_ID with results === false
No Class_ID with results === array()
Currently the functions sorts on results completely fine, but not on whether a search has a Class_ID.
usort($searches, 'sort_searches')

From the PHP 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.
Your function is not returning an integer.
To spell it out, let's say we wanted to write a sorting function for numbers (totally unnecessary, but for the exercise):
function sort_nums($a, $b)
{
if ($a < $b) return -1; // $a is less than $b
if ($a > $b) return 1; // $a is greater than $b
return 0; // $a is equal to $b
}

Related

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);

Spaceship operator confusion in PHP7 (return -1)

I am pretty new to PHP7 and so far it seems great and powerful. I have been using PHP5.6 so I started understanding the usage of spaceship operator <=>. But somehow I couldn't get the logic that statement returns -1. I know the point of returning to 0 or 1 which are false or true. Can anyone clarify the usage of return -1?
Function normal_sort($a, $b) : int
{
if( $a == $b )
return 0;
if( $a < $b )
return -1;
return 1;
}
function space_sort($a, $b) : int
{
return $a <=> $b;
}
$normalArray = [1,34,56,67,98,45];
//Sort the array in asc
usort($normalArray, 'normal_sort');
foreach($normalArray as $k => $v)
{
echo $k.' => '.$v.'<br>';
}
$spaceArray = [1,34,56,67,98,45];
//Sort it by spaceship operator
usort($spaceArray, 'space_sort');
foreach($spaceArray as $key => $value)
{
echo $key.' => '.$value.'<br>';
}
You have three possibilities when comparing the two values that are passed to a comparison function: $a < $b, $a == $b, or $a > $b. So you need three distinct return values and PHP has chosen the integers: -1, 0, and 1. I guess it could just as easily be strings lesser, equal and greater or integers 5, 7 and 9 or any combination, but it's not.
From the manual usort()
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.
$a < $b return -1
$a == $b return 0
$a > $b return 1
This is NOT how types work in PHP, but you can think of it like this: is $a > $b? where -1 means false, 1 means true and 0 means neither (equal).

How to differentiate falsey array values from falsey out-of-bounds indices in php?

In what ways can one determine whether the value of an array's index is falsey due to an out-of-bounds index in PHP?
For example, one might erroneously conclude that both $a and $b below have at least three items each, since the comparison of the third items in both arrays purport to have identical values at index 2, when in reality there is no index 2 in array $b. Indeed both values are null.
$a = array(1, false, null, 1, 0);
$b = array(true, 0);
echo (int)($a[0] === $b[0]); // 0
echo (int)($a[1] === $b[1]); // 0
echo (int)($a[2] === $b[2]); // 1 null<declared>===null<index-out-of-bounds>
echo (int)($a[3] === $b[3]); // 0
echo (int)($a[4] === $b[4]); // 0
Interesting question... Maybe somthing like this (but only for numeric array indexes):
(int)(count($a)>=$i && count($b)>=$i && $a[$i]===$b[$i])
?
EDIT: probably better solution based on array_key_exists:
(int)(array_key_exists($i, $a) && array_key_exists($i, $b) && $a[$i]===$b[$i] )

PHP equal variables

I wondered if there is any way to check if any of a large amount of variables are equal.
If I only have a few variables I can just do this:
if ($a == $b || $a == $c || $b == $c)
However, if I have 20 variables it will take some time to write all combinations. Is there another method?
if (count(array_unique(array($a, $b, $c), SORT_REGULAR)) === 1) {
// all equal
}
All this code does is place the variables in an array and eliminates duplicates. If they are all equal the result of array_unique() should be an array with one value.
If you want to make sure all of them are different it's not much different. Just check to see if the filtered array is the same size as the original array:
$array = array($a, $b, $c);
if (count(array_unique($array, SORT_REGULAR)) === count($array)) {
// all not equal
}

php array_multisort not working

I'm having this issue where I want to sorty a multidimensional array based on 2 parameters
I build my array like this:
$teamList[$t['id']] = array(
'id' => $t['id'],
'name' => $t['name'],
'score' => $score,
'points' => $array
);
I then sort like this:
foreach ($teamList as $key => $row) {
$score[$key] = $row['score'];
$points[$key] = $row['points'];
}
array_multisort($score, SORT_DESC, $points, SORT_DESC, $teamList);
But the $teamList remains unsorted?
You can easily use a user defined compare function instead of doing all the copying of values and abusing array_multisort().
function sortByScoreAndPoints($a, $b) {
if ($a['score'] == $b['score']) {
if ($a['points'] == $b['points']) {
return 0;
}
return ($a['points'] > $b['points']) ? -1 : 1;
}
return ($a['score'] > $b['score']) ? -1 : 1;
}
uasort($teamlist, 'sortByScoreAndPoints');
The sort function has to accept two parameters which can have arbitrary names, but $a and $b is used in the docs. During sorting, PHP passes any two values of the array as $a and $b and expects an answer in which order they should appear. Your sort function has to return -1 if $a should appear first, 0 if they are equal, or 1 if $a should appear last, compared to $b.
My code first tests if the scores are equal. If not, the last return will compare which score is higher ( $a > $b ), and the highes score goes into the list first (if a is bigger than b, return -1 to say a goes first).
If the scores are equal, points will be tested. If they are not equal, the comparison takes place again. Otherwise 0 is returned.
Any entry in the team list with equal score and points might appear in arbitrary location in the result (but not random - the same input array will always be sorted the same), because there is no further ordering specified. You might easily extend your sorting by adding another comparison for the name or the id, if you like.
If you want your sorted array to be renumbered starting at 0, use usort() instead of uasort().

Categories