I have tried adapting this code to use to sort a multidimensional array on a named key/field. The field is an integer what I need to sort smallest to biggest.
function myCmp($a, $b)
{
return strcmp($a["days"], $b["days"]);
}
uasort($myArray, "myCmp");
This sorts the arrays as I need but in the wrong order. At the moment it sorts biggest to smallest, not using natural order. I need to sort smallest to biggest in natural order (eg 2 comes before 5, 12 and 24).
strnatcmp() is your friend
e.g. (using a php 5.3 closure/anonymous function):
<?php
$myArray = array( 'foo'=>array('days'=>2), 'bar'=>array('days'=>22), 'ham'=>array('days'=>5), 'egg'=>array('days'=>12) );
uasort($myArray, function($a, $b) { return strnatcmp($a["days"], $b["days"]); });
foreach($myArray as $k=>$v) {
echo $k, '=>', $v['days'], "\n";
}
prints
foo=>2
ham=>5
egg=>12
bar=>22
You can just reverse the parameters of strcmp :
function myCmp($a, $b)
{
return strcmp($b["days"], $a["days"]);
}
uasort($myArray, "myCmp");
Since you want to sort in natural order you should not be using strcmp, you can do:
function myCmp($a, $b)
{
if ($a['days'] == $b['days']) return 0;
return ($b['days'] > $a['days']) ? -1 : 1;
}
Here is a working example.
Related
I have an associative array in PHP that is created from a SQL Query. I've been sorting by just one column, but now I need to change the code to sort by two columns.
The two columns consist of a FLOAT and a STRING:
$array["FinalValue"] = float and
$array["Category"] = string
I'm using this right now to just sort by the one column (FinalMulti) which is a FLOAT:
usort($categorylist, function($a, $b){
if((float) $b['FinalMulti'] == (float) $a['FinalMulti']) return 0;
return ((float) $b['FinalMulti'] < (float) $a['FinalMulti']) ? - 1 : 1;
});
I've searched the site on how to do this and found an example that sorted by STRING then INTEGERS, but nothing quite in my order and nothing that builds on the code I'm currently using.
The other examples used strcmp and I've inserted it in few place and it doesn't work. For example, I tried:
usort($categorylist, function($a, $b){
if((float) $b['FinalMulti'] == (float) $a['FinalMulti']) {
return strcmp($b['CategoryName'], $a['CategoryName']);
}
return ((float) $b['FinalMulti'] < (float) $a['FinalMulti']) ? - 1 : 1;
});
I know strcmp compares strings so I feel like that there should be an if statement, but I don't know what I would return. While I'm decent at programing, my understanding of usort is not strong.
In the end, ["FinalMulti"] needs to be sorted DESC from highest to lowest while ["CategoryName"] needs to be sorted in alphabetical order (A to Z). The first letter in all ["CategoryName"] strings are capitalized.
Thanks in advance!
usort($categorylist, function($a, $b) {
if ((float)$b['FinalMulti'] == (float)$a['FinalMulti']) {
return strcmp($a['Category'], $b['Category']);
}
return ((float)$b['FinalMulti'] < (float)$a['FinalMulti']) ? -1 : 1;
});
You can use array_multisort with array_column:
array_multisort(
array_column($categorylist, 'FinalMulti'), SORT_DESC,
array_column($categorylist, 'CategoryName'), SORT_ASC,
$categorylist
);
3v4l.org demo
I have below json outout. Can anyone please suggest how to sort this according to "no_count" column?
$arr= {"UserHeader":[
{"id":"154", "no_count":15},
{"id":"155", "no_count":11},
{"id":"158", "no_count":13},
{"id":"159", "no_count":31},
{"id":"164", "no_count":11}
]}
I have used USORT but no luck. The code is not working and give me the same array without sorting.
usort($arr, function($a, $b) { //Sort the array using a user defined function
return $a->no_count > $b->no_count ? -1 : 1; //Compare the scores
});
print_r($arr);
Might be a small formatting error.
Thanks
I did some corrections :
$arr = json_decode('{"UserHeader":[
{"id":"154", "no_count":15},
{"id":"155", "no_count":11},
{"id":"158", "no_count":13},
{"id":"159", "no_count":31},
{"id":"164", "no_count":11}
]}');
usort($arr->UserHeader, function ($a, $b) { //Sort the array using a user defined function
return $a->no_count > $b->no_count ? -1 : 1; //Compare the scores
});
print_r($arr);
It should work as expected.
I'm reading from a text file that has 2 columns. name,rank So it looks like this:
quxerm,6
brock,5
chris,15
So the 2d array looks like [0][0] = quxerm and [0][1]=6 [1][0] = brock [1][1]=5
I already have them into a 2d array like I showed above.
I need to sort these values in descending order by the integer column. How can I sort this?
#CBergau's answer is almost perfect but the order will be ascending instead of descending.
To get it descending just switch the return values of the compare function which is called by usort. See http://www.php.net/manual/en/function.usort.php for more information.
function cmp(array $a, array $b) {
return ($a[1] < $b[1]) ? 1 : -1;
}
usort($arr, 'cmp');
Example: http://codepad.org/QRTQLxTh
You could also extend the compare function for example to order ascending by name when the rank is the same by using strcmp. See http://www.php.net//manual/en/function.strcmp.php for more information.
function cmp(array $a, array $b) {
if ($a[1] == $b[1]) {
return strcmp($a[0], $b[0]);
}
return ($a[1] < $b[1]) ? 1 : -1;
}
Example: http://codepad.org/SeRTE3Ym
Note: I've not enough reputation yet to just comment on #CBergau's answer.
Use map instead, and then you can use all the functions relatives to maps like this.
take the array as arr[i][j].
try comparing only by changing the values of i.
for(int i=0;i<3;i++)
for(int k=1;k<3;k++)
if(arr[i][1]>arr[k][1])
max=i
and you can retrieve max by :-
arr[max][0]//name of max
arr[max][1]//value of max
$sorted = array();
foreach($yourArray as $a){
$sorted[$a[1]] = $a[0];
}
ksort($sorted);
vardump($sorted);
This should sort by the integer column:
usort(
$data,
function ($arrayOne, $arrayTwo) {
return ($arrayOne[1] < $arrayTwo[1]) ? -1 : 1;
}
);
If there are no duplicate names, you can simply assign the rank for the key (name), and sort that array while preserving keys.
$data["quxerm"] = 6;
$data["brock"] = 5;
$data["chris"] = 15;
asort($data, SORT_NUMERIC);
I'm trying to do the same as MySQL query
SELECT * FROM table ORDER BY field1, field2, ...
but with php and a multidimensional array:
$Test = array(
array("a"=>"004", "n"=>"03"),
array("a"=>"003", "n"=>"02"),
array("a"=>"001", "n"=>"02"),
array("a"=>"005", "n"=>"01"),
array("a"=>"001", "n"=>"01"),
array("a"=>"004", "n"=>"02"),
array("a"=>"003", "n"=>"01"),
array("a"=>"004", "n"=>"01")
);
function msort(&$array, $keys){
array_reverse($keys);
foreach($keys as $key){
uasort($array, sortByKey);
}
//
function sortByKey($A, $B){
global $key;
$a = $A[$key];
$b = $B[$key];
if($a==$b) return 0;
return ($a < $b)? -1 : 1 ;
}
}
//
msort($Test, array("a","n"));
//
foreach($Test as $t){
echo('<p>'.$t["a"].'-'.$t["n"].'</p>');
}
My theory is: if I sort multiple times on columns with "lesser importance" then columns of "greater importance", I'll achieve an order like the above MySQL query.
Unfortunately, php is returning:
Warning: uasort() expects parameter 2 to be a valid callback, function 'sortByKey' not found or invalid function name in /Library/WebServer/Documents/www/teste.array_sort.php on line 23" (uasort line)
It's a simple order function. What am I missing?
Fundamentally we're going to use the same approach as explained here, we're just going to do it with a variable number of keys:
/**
* Returns a comparison function to sort by $cmp
* over multiple keys. First argument is the comparison
* function, all following arguments are the keys to
* sort by.
*/
function createMultiKeyCmpFunc($cmp, $key /* , keys... */) {
$keys = func_get_args();
array_shift($keys);
return function (array $a, array $b) use ($cmp, $keys) {
return array_reduce($keys, function ($result, $key) use ($cmp, $a, $b) {
return $result ?: call_user_func($cmp, $a[$key], $b[$key]);
});
};
}
usort($array, createMultiKeyCmpFunc('strcmp', 'foo', 'bar', 'baz'));
// or
usort($array, createMultiKeyCmpFunc(function ($a, $b) { return $a - $b; }, 'foo', 'bar', 'baz'));
That's about equivalent to an SQL ORDER BY foo, bar, baz.
If of course each key requires a different kind of comparison logic and you cannot use a general strcmp or - for all keys, you're back to the same code as explained here.
Here's some code I wrote to do something similar:
uasort($array,function($a,$b) {
return strcmp($a['launch'],$b['launch'])
?: strcmp($a['tld'],$b['tld'])
?: strcmp($a['sld'],$b['sld']);
});
It kind of abuses the fact that negative numbers are truthy (only zero is falsy) to first compare launch, then tld, then sld. You should be able to adapt this to your needs easily enough.
PHP7.4's arrow syntax eliminates much of the code bloat which was previously necessary to bring your column orders into the usort() scope.
Code: (Demo)
$orderBy = ['a', 'n'];
usort($Test, fn($a, $b) =>
array_map(fn($v) => $a[$v], $orderBy)
<=>
array_map(fn($v) => $b[$v], $orderBy)
);
var_export($Test);
I reckon this is a very elegant and concise way to script the task. You generate the nominated column values from $a and $b as separate arrays and write the spaceship operator between them.
Without the arrow syntax, the snippet gets a little more chunky.
Code: (Demo)
$orderBy = ['a', 'n'];
usort($Test, function($a, $b) use ($orderBy) {
return
array_map(function($v) use ($a){
return $a[$v];
}, $orderBy)
<=>
array_map(function($v) use ($b){
return $b[$v];
}, $orderBy);
});
var_export($Test);
The spaceship operator will walk through corresponding pairs of data ([0] vs [0], then [1] vs [1], and so on) until it reaches a non-zero evaluation or until it exhausts the comparison arrays.
With fewer total iterated function calls, you could call array_multisort() after preparing flat columns to sort by.
Code: (Demo)
$orderBy = ['a', 'n'];
$params = array_map(fn($colName) => array_column($Test, $colName), $orderBy);
$params[] = &$Test;
array_multisort(...$params);
var_export($Test);
This will do the job, thanks for contributions!
function mdsort(&$array, $keys){
global $KeyOrder;
$KeyOrder = $keys;
uasort($array, cmp);
}
function cmp(array $a, array $b) {
global $KeyOrder;
foreach($KeyOrder as $key){
$res = strcmp($a[$key], $b[$key]);
if($res!=0) break;
}
return $res;
}
//
mdsort($Test, array("a","n"));
This code is a little ugly, though, I believe it can be better - maybe a class to solve the issue of passing the array with the keys to the "cmp" function. But it's a start point, you can use it with any number of keys to sort.
I am creating a search capability for a forum program I'm building. At one point I have an array called searchResults which is numeric and contains 'score' as one of its dimensions (others are 'timeStarted', 'authorUID', and 'TID')
So, this array is in disarray at this point and I need to organize it to where $searchResults[1] will be the highest 'score' and [2] will have the second highest, etc. I looked at array_multisort on php.net but got rapidly lost in how it worked. So how would I sort $searchResults in numeric order (rearrange the keys) descending with a descending order of a further dimension 'sort' as the sorting mechanism? There really isn't any code to go with it but if you need a layout of how the array looks, here you go:
$searchResults:
[1] - ['timeStarted']
['authorUID']
['score'] <- Need to sort first dimension by this value descending
['TID']
etc.
Thanks for any help.
usort allows sorting by any specified comparison function
$cmp = function($a,$b) {return $b['score'] - $a['score'];};
usort($searchResults, $cmp);
In this case, $cmp is a function that compares two elements $a and $b of $searchResults based on value of ['score']. $cmp returns 0 for equality, negative val if $a['score'] is greater, positive val if $b['score'] is greater. It would normally be the other way around, but a descending sorting order is desired.
The function you want is usort. Look at example #2:
<?php
function cmp($a, $b)
{
return strcmp($a["fruit"], $b["fruit"]);
}
$fruits[0]["fruit"] = "lemons";
$fruits[1]["fruit"] = "apples";
$fruits[2]["fruit"] = "grapes";
usort($fruits, "cmp");
while (list($key, $value) = each($fruits)) {
echo "\$fruits[$key]: " . $value["fruit"] . "\n";
}
?>
The output:
$fruits[0]: apples
$fruits[1]: grapes
$fruits[2]: lemons