I am trying to get PHP to compare two values and sort them using usort(). I have this function, which works, however this function stops running if $a == $b,
Having this function stop running prevents further functions in the PHP file to run.
<?php
function cmp($a, $b) {
if ($a[4] == $b[4]) {
return 0;
}
return ($a[4] < $b[4]) ? -1 : 1;
}
usort($participants_times,"cmp");
?>
When there are two values that are equal, I don't mind which one is in front of the other. I have tried setting return -1, return 0, and return 1 when $a == $b but they didnt work for me.
Any help is appreciated :)
You should replace ternary operator with nested if-else statements. In your condition, it returns 1 for both > and == comparison.
if ($a[4] < $b[4])
return -1;
else if ($a[4] > $b[4])
return 1;
else
return 0;
because you don't care the equal case, just ignore it
function cmp($a, $b) {
return ($a[4] < $b[4]) ? -1 : 1;
}
usort($participants_times,"cmp");
So the answers provided are very likely correct to my question - however, in my case, the issue was related to the form of my function()'s where I had a function inside a function and a second iteration of the initial function failed.
Moving my cmp() function outside of the function that calls it solved my issue.
Related
So this function works great for sorting array or object by 1 value
function sort( $a, $b )
{
if( $a['1'] == $b['1'] ){ return 0 ; }
return ($a['1'] < $b['1']) ? -1 : 1;
}
usort($myArray,'sort');
But I would like it to sort by value 1 primary and by value 2 second, if this is possible with this function, or is it only doable with a custom function?
Sure, it's possible. To add an second sorting expression, you should extend the equal case $a['1'] == $b['1'].
You should also notice, you don't have to return exactly -1 or 1 - you could use any negative/positive number. So your sorter could be written as the following
usort(function($a, $b) {
if($a['1'] == $b['1']) {
return $a['2'] - $b['2'];
}
return $a['1'] - $b['1'];
}, 'sort');
I used this function. It is probably not the most efficient one, but for now it works.
function comp($a, $b) {
if ($a['1'] == $b['1']) {
return ($a['2'] < $b['2']) ? -1 : 1;
}
return strcmp($a['1'], $b['1']);
}
usort($arr, 'comp');
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.
I have a big array with a lots of datas, here you can have an example :
Is this example you have only one item [0].
I need to sort this array by the key date in last_message. (more recently in first)
In date i have this format : "2014-04-23T14:59:53+0200"
Do you have a good idea for me ? I don't want to foreach i think there is something better.
Thanks !
This is my code :
uasort($arrayC, function ($a, $b) {
if ($a == $b) {
return 0;
}
return (strtotime($a['last_message']->date) < strtotime($b['last_message']->date)) ? -1 : 1;
});
But there is no effect on my array..
You can use http://www.php.net/manual/en/function.uasort.php and provide it with a custom function that compares the date.
This would look something like this:
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return (strtotime($a['last_message']['date']) < strtotime($b['last_message']['date'])) ? -1 : 1;
}
uasort($array, 'cmp');
You'll need to use PHP's uasort() function. The manual page is at http://www.php.net/manual/en/function.uasort.php
I have my code working perfectly how I want it to, but the problem is my code is sorting by highest value to lowest. Can you help me reverse it so that when I print out the first 10 it is actually the "10 newest" (meaning the lowest duration)?
Thanks so much
function compareStreamDurations($a, $b)
{
if ($a["duration"] == $b["duration"])
{
return 0;
}
return ($a["duration"] > $b["duration"]) ? -1 : 1;
}
usort($onlineStreams, 'compareStreamDurations');
for ( $i=0; $i<10; $i++ )
{
echo '<p>', $onlineStreams[$i]["duration"] ,'</p>';
}
The solutions posted below (reversing the sign) are NOT working. I'm doing a print_r of $onlineStreams before and after the usort function call and they are both the same.
Just change your greater than to a less than:
return ($a["duration"] < $b["duration"]) ? -1 : 1;
Try reversing the greater than sign and making it a less than, like this:
function compareStreamDurations($a, $b)
{
if ($a["duration"] == $b["duration"])
{
return 0;
}
return ($a["duration"] < $b["duration"]) ? -1 : 1;
}
So we got this function in PHP
strcmp(string $1,string $2) // returns -1,0, or 1;
We Do not however, have an intcmp(); So i created one:
function intcmp($a,$b) {
if((int)$a == (int)$b)return 0;
if((int)$a > (int)$b)return 1;
if((int)$a < (int)$b)return -1;
}
This just feels dirty. What do you all think?
this is part of a class to sort Javascripts by an ordering value passed in.
class JS
{
// array('order'=>0,'path'=>'/js/somefile.js','attr'=>array());
public $javascripts = array();
...
public function __toString()
{
uasort($this->javascripts,array($this,'sortScripts'));
return $this->render();
}
private function sortScripts($a,$b)
{
if((int)$a['order'] == (int)$b['order']) return 0;
if((int)$a['order'] > (int)$b['order']) return 1;
if((int)$a['order'] < (int)$b['order']) return -1;
}
....
}
Sort your data with:
function sortScripts($a, $b)
{
return $a['order'] - $b['order'];
}
Use $b-$a if you want the reversed order.
If the numbers in question exceed PHP's integer range, return ($a < $b) ? -1 : (($a > $b) ? 1 : 0) is more robust.
Purely as some additional information, there has been an accepted RFC for this (https://wiki.php.net/rfc/combined-comparison-operator).
So, the comparison function would be along the lines of ...
<?php
$data = [...];
usort($data, function($left, $right){ return $left <=> $right; });
?>
A few really nice feature here is that the comparison is done in exactly the same way as all other comparisons. So type juggling will happen as expected.
As yet, there is no magic __forCompare() like method to allow an object to expose a comparison value. The current proposal (a different RFC) is to have each object be injected into every other object during the comparison so that it does the comparison - something which just seems odd to me - potential opportunity for recursion and stack overflow ... ! I would have thought either injecting the type of object for comparison (allowing an object the ability to represent appropriate values depending upon the type of comparison) or a blind request for a value that the object can serve up for comparison, would have been a safer solution.
Not yet integrated into PHP-NG (PHP 7 at the moment), but hopefully will be soon.
why reinventing the wheel?
http://php.net/manual/en/function.strnatcmp.php
echo strnatcmp(1, 2) . PHP_EOL; // -1
echo strnatcmp(10, 2) . PHP_EOL; // 1
echo strnatcmp(10.5, 2) . PHP_EOL; // 1 - work with float numbers
echo strnatcmp(1, -2) . PHP_EOL; // 1 - work with negative numbers
Test it here:
https://3v4l.org/pSANR
You could use
function intcmp($a,$b)
{
return ($a-$b) ? ($a-$b)/abs($a-$b) : 0;
}
Although I don't see the point in using this function at all
Does it have to be +1 and -1? If not, just return (int) $a - (int) $b. I don't like the divide that someone else recommended, and there's no need to check for all three cases. If it's not greater and not equal, it must be less than.
return (int) $a > (int) $b ? 1 : (int) $a == (int) $b ? 0 : -1;
At a glance, yes it feels dirty. Except there must be a good reason you wrote that instead of just using the actual ==, >, and < operators. What was the motivation for creating this function?
If it were me, I'd probably just do something like:
$x = $a==$b ? 0 : ($a>$b ? 1 : ($a<$b ? -1 : null));
I realize this is just as ugly, and the : null; - not sure if PHP requires it or if I could have just done :; but I don't like it and that code should never execute anyway... I think I'd be a lot less confused about this if I knew the original requirements!
For strings
usort($points, function ($a, $b) use ($orderFlag, $key1, $key2) {
return strcmp($a[$key1][$key2], $b[$key1][$key2]) * $orderFlag;
});
orderFlag => 1 (ascending): -1 (descending)
For numbers
usort($points, function ($a, $b) use ($orderFlag, $key1, $key2) {
return ($a[$key1][$key2] - $b[$key1][$key2]) * $orderFlag;
});
orderFlag => 1 (ascending): -1 (descending)