Sort array by values ASC, then keys ASC [duplicate] - php

This question already has answers here:
Sort a flat, associative array by numeric values, then by non-numeric keys
(8 answers)
Closed 6 months ago.
SO,
The problem
I have an issue with custom array sorting. I have an array:
$rgData = [
3 => 1,
5 => 0,
1 => 2,
9 => 0,
0 => 1,
4 => 2,
2 => 1,
7 => 0,
6 => 0,
8 => 0,
];
-it contain keys with their counts (actually, it came after some operations with array_count_values). Now I want to sort it that:
Lower values comes first (i.e. usual ascending sort which can be done with asort())
Within one value, keys should be sorted ascending (here I need help)
For sample above result should be:
[5=>0, 6=>0, 7=>0, 8=>0, 9=>0, 0=>1, 2=>1, 3=>1, 1=>2, 4=>2]
My approach
I have no idea how to resolve this with sort by user-defined since usort or uasort accept only values for comparing while uksort - only keys and I need both of them in my comparison function. The only way that I have now - is to do this:
$rgData = ['3'=>1, '5'=>0, '1'=>2, '9'=>0, '0'=>1, '4'=>2, '2'=>1, '7'=>0, '6'=>0, '8'=>0];
$rgTemp = [];
asort($rgData);
$i = 0;
$mPrev = current($rgData);
foreach($rgData as $mKey=>$mValue)
{
$rgTemp[$mPrev==$mValue?$i:++$i][$mKey] = $mValue;
$mPrev = $mValue;
}
$rgTemp = array_map(function($rgX)
{
ksort($rgX);
return $rgX;
}, $rgTemp);
$rgData = [];
//can't use call_user_func_array('array_merge', $rgTemp) - it spoils numeric keys
foreach($rgTemp as $rgX)
{
foreach($rgX as $mKey=>$mValue)
{
$rgData[$mKey] = $mValue;
}
}
//var_dump($rgData);
-i.e. split array by values first, then do the stuff.
The question
How to do this in more easy way? I used asort + cycled ksort via array_map with final gathering loop. Temp array also used. It looks weird. I hope simpler method exists.

This should do what you want. It sorts by the keys, but we can access the value inside the function, so we can use that as our sort criteria too.
uksort($rgData, function($a, $b) use($rgData){
$aVal = $rgData[$a];
$bVal = $rgData[$b];
// Compare the values
if($aVal === $bVal){
// If they are the same, compare the keys
return $a - $b;
}
// Otherwise compare the values
return $aVal - $bVal;
});

Related

Sorting simple Object [duplicate]

This question already has answers here:
Sort a flat, associative array by numeric values, then by non-numeric keys
(8 answers)
Closed 1 year ago.
This post was edited and submitted for review 1 year ago and failed to reopen the post:
Original close reason(s) were not resolved
Base material :
I have a base list $food :
[{"id":"123","name":"apple"},{"id":"456","name":"Rost Beef"},...]
that i first reduce with $food_reduced = array_count_values(array_columns($food, "id"));.
[{123:2},{456:5},...]
Then i loop through $food_reduced with foreach($food_reduced as $id => $count) to query a DataBase to get the category (vegetables, fruits, meat, fish, etc..) that i store into $food_byCategory = array(); declared right before the loop :
if ($food_byCategory[$row["ctg_name"]])
{
$food_byCategory[$row["ctg_name"]] += $count;
}
else
{
$food_byCategory[$row["ctg_name"]] = $count;
}
Which gives me this array that I want to sort (gettype() says it's an Array at least) in PHP :
{
Vegetable: 2
Fruit: 1
Fish: 5
Drinks: 1
Meat: 2
Desert: 3
}
to this :
{
Fish: 5
Desert: 3
Meat: 2
Vegetable: 2
Drinks: 1
Fruit: 1
}
Notice the sorting by key once the sorting by value is done.
The goal is then to display this list into an html list by value descending.
I have tried asort, sort, arsort, with or without the option SORT_NUMERIC etc...
Wether it's not working at all or it's erasing the keys and replacing them with indexes.
I've also tried typecasting it into an array, but nothing changed (probably because it is an Array already ? But yet, not an Object ?)
I'm kinda lost.
Help ?
SOLVED my issue by building the "count" array another way :
if (!in_array($ctg_id, array_column($food_byCategory, "id")))
{
$food_byCategory[] = array(
"id" => $ctg_id,
"count" => 1
);
}
else
{
$index = array_search($ctg_id, array_column($food_byCategory, "id"));
$food_byCategory[$index]["count"]++;
}
and then sorting it with usort :
usort($food_byCategory, function($a, $b) {
return $b["count"] <=> $a["count"];
});
asort() applies to associative arrays, not objects.
You can easily transform your object into a associative array by typecasting it as describe here.
$array = (array) $yourObject;
You can use a php function called sort, try something like this:
$myArray = array(4, 6, 2, 22, 11);
sort($myArray);
result: 2, 4, 6, 11, 22
or
$myArray = array(4, 6, 2, 22, 11);
rsort($myArray);
result: 22, 11, 6, 4, 2

How to allow same keys in one array? [duplicate]

This question already has answers here:
PHP Associative Array Duplicate Keys
(6 answers)
Closed 4 years ago.
I need to add the same keys to the array, but with different values,
foreach ($selections as $selection) {
$array += [$selection['option_id']=>$selection['product_id']];
}
// example output
$array = [30=>12,14=>10],
but really it should be
[30=>7,30=>12,14=>10];
When the key repeats, it merges.
You just can't.
But you can make the value of this key an array.
So you'll have
$array = [30=>[7,12],14=>10];
You can use any array functions on $array[30]
What you should do is to return the products ids as an array:
$array = array_reduce($selections, function ($carry, $selection) {
if (!isset($carry[$selection['option_id']])) {
$carry[$selection['option_id']] = [];
}
$carry[$selection['option_id']][] = $selection['product_id'];
return $carry;
}, []);
Now the result would be:
[30 => [7, 12], 14 => [10]];
Keys in array are, as the word itself says, keys to access the value they contain and each key must be unique, else you won't have a way to . If you could have two time or more the same value, how could you tell which will access one value and which one will access the other one? To solve your problem you have a way: generate a multidimensional array such that you can have multiple value stored "behind" a single key. E.g. [30 => [7,12], 14 => 10]
Based on your code you can just create a double loop with a nested foreach to navigate through all the value, something like:
foreach ($selections as $selection) {
if(!is_array($selection['product_id']) $array += [$selection['option_id']=>$selection['product_id']];
else {
foreach ($selection['product_id'] as $product) {
$array += [$selection['option_id']=> product];
}
}
}

PHP Multisort array on more than 2 criteria [duplicate]

This question already has answers here:
How can I sort arrays and data in PHP?
(14 answers)
Closed 7 years ago.
I wrote a php script that will execute a full (dutch) soccer competition, based on parameters as Strength and moral.
After all rounds have been 'played', I use usort to define the end results.
These results are based on the 'Points' field. However, when two teams share the same number of points, further ranking has to be achieved by comparing the goal difference.
I tried to accomplish this sorting using first sorting om 'Points' and after that sorting om 'GoalDifference (GD)'. Unfortunately in the latter case goaldifference is sorted correctly but not the Points...
This is what the teams array looks like now:
$teams = array
(
array(
'Id' => 1,
'Teamname' => 'Team 1,
'Strength' => 22,
'Moral' => 15,
'Points' => 0,
'Pro' => 0,
'Contra'=> 0,
'GD' => 0
)
}
Below the usort functions
usort($teams, function($a, $b) {
return $a['Points'] < $b['Points'];
});
usort($teams, function($a, $b) {
return $a['GD'] < $b['GD'];
});
So my question is, what is the best way to first sort on points and after that on goaldifference?
Kind regards,
Kees
You can build an more complex sort function where you check the columns in priority.
usort($teams, function($a, $b) {
if ($a["Points"] < $b["Points"]) return -1;
if ($a["Points"] > $b["Points"]) return 1;
if ($a['GD'] < $b['GD']) return -1;
if ($a['GD'] > $b['GD']) return 1;
return 0;
});
Include both comparisons at the same time. So if the points are the same, use goal difference
usort($teams, function($a, $b) {
return ($a['Points'] === $b['Points']
? $a['GD'] < $b['GD']
: $a['Points'] < $b['Points']);
});
You can use array_multisort() function.
$points = array();
$gd = array();
foreach ($teams as $key => $row) {
$points[$key] = $row['Points'];
$gd[$key] = $row['GD'];
}
array_multisort($points, SORT_DESC, $gd, SORT_DESC, $teams);

php sort an array by value and for equal values, sort by key

I have an array
(0=>0,1=>3,2=>3)
I have to sort this array first by values, and for equal values , I have to sort by order of increasing key first.
I have tried to use multisort over the array_keys(SORT_DESC) and array_values(SORT_ASC) separately, but that gives me:
(0=>0,1=>3,2=>3)
but I want
(0=>0,2=>3,1=>3)
You can always use simple callback to sort it. Using uksort() it will be:
$input = array(0 => 0, 2 => 3, 1 => 3);
uksort($input, function($x, $y) use ($input)
{
if($input[$x]==$input[$y])
{
return $x<$y?-1:$x!=$y;
}
return $input[$x]-$input[$y];
});

How to check if values of a numeric 2D PHP array are in 'incereasing' , 'descending' , or 'mixed' order?

I am looking for a method to check if values of a numeric 2D PHP array are in
incereasing ,
descending ,
or
mixed order.
Example:
array( 1, 2, 3, 4 ) // This is an incereasing numeric array
array( 4, 3, 2, 1 ) // This is a descending numeric array
array( 1, 3, 2, 4 ) // This is a mixed numeric array
How could I check it? ( i am looking for a fast method, it needs to run qickly )
I think, if you are looking for quick solution (i.e. works quick), you'll have to work with your data array like:
function getOrder($rgData)
{
if(!count($rgData) || count($rgData)==1)
{
return null;
}
$sCurrent = (string)array_shift($rgData);
$iOrder = current($rgData)>$sCurrent?1:-1;
foreach($rgData as $mValue)
{
if(($sCurrent>(string)$mValue && $iOrder== 1) ||
($sCurrent<(string)$mValue && $iOrder==-1))
{
return 0;
}
$sCurrent = (string)$mValue;
}
return $iOrder;
}
this will return 1,0 and -1 for corresponding ascending, mixed and descending order. Note, that all values will be treated and compared as strings. This method is more useful since it have O(N) complexity (at worst case) while using sort() function will result in O(N log(N)) complexity (at best case)
Assuming your variable is called $array this might start in the right direction:
$tempArray=sort($array);
if($array==$tempArray)
{
// Array is in acsending order
}
$tempArray=arsort($array);
if($array==$tempArray)
{
// Array is in desending order
}
If neither of these match, it's mixed.
Edit: Thanks to Alma Do Mundo, Corrected code.

Categories