Php Sorting arrays - php

I have a key => value array:
a => 2
c => 1
b => 3
I tried this:
ksort($result);
arsort($result);
But it doesn't work. I'm trying to sort by key alphabetically a-z and then sort it by value ascending 0-infinity.
so I should get
c => 1
a => 2
b => 3
But those sorts didn't give me what I wanted.

Try using asort() instead of arsort(). arsort() will sort the array in reverse order. Something like this should "work":
$test = array(
'a' => 0,
'b' => 1,
'c' => 2
);
ksort($test);
asort($test);
Mario is correct that this won't work if multiple items contain the same value. Alternatively, you could use uksort() which allows you to define exactly how the array is sorted. For example you could sort two items using their values by default. But if the values are the same sort by their keys.
$test = array(
'a' => 2,
'd' => 1,
'c' => 1,
'b' => 3
);
function cmp($a, $b){
global $test;
$val_a = $test[$a];
$val_b = $test[$b];
if($val_a == $val_b){
return ($a < $b) ? -1 : 1;
}
return ($val_a < $val_b) ? -1 : 1;
}
uksort($test, 'cmp');

I get unexpected results because sorting values that have the same value is unstable.
So what you forgot to mention in your question is that values can occur twice, and you want arrays sorted by values and keys secondarily.
c => 1
a => 2
z => 2
b => 3
There's no function for that in PHP. You could however try to sort by keys first ksort(), and then apply a user-defined function for sorting by value uasort(). In the callback it's important to also implement the $a == $b check and return 0. So the previous key-ordering might not be accidentally altered by +1 or -1 comparison states. (Don't know if that actually works.)
Otherwise you'll have to implement the whole sorting algorithm yourself, possibly separating keys and values in distinct maps.

Related

Shuffle array with a repeatable/predictable result based on a seed integer

I have 6 users and I want to shuffle an array for each user but with a particular logic.
I have array like this:
$a = array(1, 6, 8);
When shuffled it gives me these results:
shuffle($a); //array(8,6,1) or array(8,1,6) or ...
I want to shuffle the array for a specific user and have it be the same every time for that user:
for user that has id equals 1, give array like this array(6,8,1) every time
for user that has id equals 2, give array like this array(1,8,6) every time
In other words, I want to shuffle an array with private key!
If you provide a seed to the random number generator it will randomize the same way for the same seed (see the version differences below). So use the user id as the seed:
srand(1);
shuffle($a);
Output for 7.1.0 - 7.2.4
Array
(
[0] => 1
[1] => 8
[2] => 6
)
Output for 4.3.0 - 5.6.30, 7.0.0 - 7.0.29
Array
(
[0] => 6
[1] => 1
[2] => 8
)
Note: As of PHP 7.1.0, srand() has been made an alias of mt_srand().
This Example should always produce the same result.
Quoting php.net:
This function shuffles (randomizes the order of the elements in) an array. It uses a pseudo random number generator that is not suitable for cryptographic purposes.
Whatever you are trying to get as a result, you cannot use shuffle because it will randomly give you some order.
If you want to randomly do an order to array, based on some criteria use usort:
function cmp($a, $b)
{
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$a = array(3, 2, 5, 6, 1);
usort($a, "cmp");
Now get the logic in the cmp function...

PHP: How to sort array by value first, then by key [duplicate]

This question already has answers here:
Sort a flat, associative array by numeric values, then by non-numeric keys
(8 answers)
Closed 2 years ago.
I've been breaking my head over the following problem.
I've got this array:
[596] => 2
[9] => 2
[358] => 2
[1579] => 1
[156] => 1
[576] => 1
[535] => 1
As you can see, the values are ordered in a descending way, but the keys are random. I would like to keys to be sorted DESC as well though. I've been playing with array_multisort, but I haven't been able to fix the problem with it. The first problem that I encountered was the fact that array_multisort reindexes numeric keys. I changed to keys to a non-numeric variant, namely k596 etc... That made me able to sort the keys, but not like I wanted it to.
[k9] => 2
[k596] => 2
[k358] => 2
[k576] => 1
[k535] => 1
[k1579] => 1
[k156] => 1
The result that I would like to see in the end is:
[k596] => 2
[k358] => 2
[k9] => 2
[k1579] => 1
[k576] => 1
[k535] => 1
[k156] => 1
Is anyone able to help me out here? There must be a simple way to do this, right?
uksort($array, function ($a, $b) use ($array) {
if ($array[$a] != $array[$b]) {
return $array[$a] - $array[$b];
}
return $a - $b;
});
Insert appropriate comparison operations, using simply - here as an example. This is somewhat trickier if you're dependent on PHP < 5.3 and don't have anonymous functions.
Ok this question is a bit more tricky then I thought! Given an array $arry = array('a'=>'hilbert', 'b'=>'noether', 'c'=>'landau');
I would generate a second array containing tuples like this:
$brry = array();
foreach($arry as $key => $value){
$brry[] = array($key,$value);
}
//Now $brry looks like:
//$brry:
// [0] => array('a','hilbert');
// [1] => array('b','noether');
// [2] => array('c','landau');
//now you can easily sort it!
usort($brry, "cmp");
//And then transform it back to the array structure you have before
foreach($brry as $value){
$crry[$value[0]] = $value[1];
}
//with this sorting function cmp:
function cmp($first, $second){
if(strcmp($first[1], $second[1]) != 0){
return strcmp($first[1], $second[1]);
}
else{
return strcmp($first[0], $second[0]);
}
}
The function cmp sorts by strings now so strcmp("192","20") > 0 while this might not be true for integers!

Misunderstanding the behavior of array_udiff

I am having troubles to understand how array_udiff works.
According to the documentation:
array_udiff ($array1, $array2, data_compare_func)
[...] data_compare_func 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.
So considering this function:
function please_compare($a, $b) {
return $a !== $b;
};
if $a equals $b, the method will return 0, 1 otherwise (because of this).
(There is no -1 returned here and I have the feeling that the explanation comes from there but I can just compare that the values are different, not that one is greater than the other one.)
Could someone explain me array_udiff's behavior in the following snippet? I also included the output of array_diff, which is actually the behavior I was expecting?
$array1 = array('a', 'b', 'c', 'd');
$array2 = array('a', 'b', 'c');
print_r(array_udiff($array1, $array2, 'please_compare'));
/* Returns:
Array
(
[0] => a
[1] => b
[3] => d
)
*/
print_r(array_diff($array1, $array2));
/* Returns:
Array
(
[3] => d
)
*/
array_udiff relies on the comparison function returning appropriate values, because it ranks the elements of your arrays. If you add some output to your comparison function, you will see that array_udiff first determines the sort order for both arrays, and only after it has done this does it start comparing array1 elements to array2 elements. By returning 1 from your comparison function, you are telling array_udiff that 'a' > 'b' and 'b' > 'a', and similarly for all other elements in both arrays. In your particular case, this causes array_udiff to think that everything in array1 > everything in array2, until it finally happens to compare the 'c' in array1 to the 'c' in array2, and gets 0 back from your function (this is why it left 'c' out of the result). See this PHP fiddle for a demonstration of the internal working of array_udiff.

How do I order an array that contains arrays? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Sort an array by a child array's value in PHP
I have the following array structure:
$en_array = array();
while(...) {
$en_array[] = $row;
}
$en_array = array (
array (
"name" => "a",
"followers" => 5,
"imageurl" => "http://images.com/img.jpg"
)
array (
"name" => "b",
"followers" => 25,
"imageurl" => "http://images.com/img.jpg"
)
array (
"name" => "c",
"followers" => 15,
"imageurl" => "http://images.com/img.jpg"
)
)
In this example I would like to order the keys of en array by the values of followers, e.g. $en_array[0]["followers"] would have the value of 25.
I'm not entirely sure if this can be done, but I hope it can.
Any help will be much appreciated :)!!
Since it looks like you're only interested in sorting by followers, we can do this easily with PHP's usort.
function compare_by_followers($a, $b) {
if($a['followers'] == $b['followers']) return 0;
return $a['followers'] > $b['followers'] ? -1 : 1;
}
usort($en_array, 'compare_by_followers');
Sorting is, at its core, a process of comparing the array's elements to each other and figuring out which ones are greater than the others. usort allows you to use a custom comparison function for this process: compare_by_followers($a, $b) returns -1 if $a['followers'] is greater than $b['followers'] (meaning that $a should go before $b), returns 1 if $a['followers'] is less than $b['followers'] (meaning that $a should come after $b), and returns 0 if they are equal.
array_multisort() is what you are after.
foreach ($en_array as $key => $row) {
$name[$key] = $row['name'];
$followers[$key] = $row['followers'];
}
array_multisort($followers,SORT_DESC,$name,SORT_ASC,$en_array);
After this, results are in descending order of followers, and where followers are the same, ascending order of name (i.e. alphabetical order).

PHP's USORT Callback Function Parameters

This is a really esoteric question, but I'm genuinely curious. I'm using usort for the first time today in years, and I'm particularly interested in what exactly is going on. Suppose I've got the following array:
$myArray = array(1, 9, 18, 12, 56);
I could sort this with usort:
usort($myArray, function($a, $b){
if ($a == $b) return 0;
return ($a < $b) ? -1 : 1;
});
I'm not 100% clear about what is going on with the two parameters $a and $b. What are they, and what do they represent. I mean, I could assume that $a represents the current item in the array, but what exactly is this getting compared to? What is $b?
I could increase my array to include strings:
$myArray = array(
array("Apples", 10),
array("Oranges", 12),
array("Strawberries", 3)
);
And run the following:
usort($myArray, function($a, $b){
return strcmp($a[0], $b[0]);
});
And that would sort my child-arrays alphabetically based upon the [0] index value. But this doesn't offer any clarity about what $a and $b are. I only know that the match the pattern that I'm seeking.
Can somebody offer some clarity about what is actually taking place?
The exact definition of $a and $b will depend upon the algorithm used to sort the array. To sort anything you have to have a means to compare two elements, that's what the callback function is used for. Some sorting algorithms can start anywhere in the array, others can start only in a specific part of it so there's no fixed meaning in $a and $b other than they are two elements in the array that have to be compared according to the current algorithm.
This method can be used to shed light upon which algorithm PHP is using.
<?php
$myArray = array(1, 19, 18, 12, 56);
function compare($a, $b) {
echo "Comparing $a to $b\n";
if ($a == $b) return 0;
return ($a < $b) ? -1 : 1;
}
usort($myArray,"compare");
print_r($myArray);
?>
Output
vinko#mithril:~$ php sort.php
Comparing 18 to 19
Comparing 56 to 18
Comparing 12 to 18
Comparing 1 to 18
Comparing 12 to 1
Comparing 56 to 19
Array
(
[0] => 1
[1] => 12
[2] => 18
[3] => 19
[4] => 56
)
From the output and looking at the source we can see the sort used is indeed a quicksort implementation, check for Zend/zend_qsort.c in the PHP source (the linked to version is a bit old, but hasn't changed much).
It picks the pivot in the middle of the array, in this case 18, then it needs to reorder the list so that all elements which are less (according to the comparison function in use) than the pivot come before the pivot and so that all elements greater than the pivot come after it, we can see it doing that when it compares everything to 18 at first.
Some further diagrammatic explanation.
Step 0: (1,19,18,12,56); //Pivot: 18,
Step 1: (1,12,18,19,56); //After the first reordering
Step 2a: (1,12); //Recursively do the same with the lesser, here
//pivot's 12, and that's what it compares next if
//you check the output.
Step 2b: (19,56); //and do the same with the greater
To sort anything you need a means to compare two items and figure out if one comes before the other. This is what you supply to usort. This function will be passed two items from your input array, and returns the order they should be in.
Once you have a means to compare two elements, you can use sort-algorithm-of-your-choice.
If you are unfamiliar, you might like to look at how a simple naive algorithm like bubblesort would use a comparison function.
Behind the scenes, PHP is using a quicksort.
usort() or uasort() have a human-feeling bug on sorted result. See the code segment:
function xxx($a,$b) { if ($a==$b) return 0; else return $a<$b?-1:1; }
$x=array(1=>10,2=>9,3=>9,4=>9,5=>6,6=>38);
uasort($x,'xxx');
print_r($x);
the result is:
Array ( [5] => 6 [4] => 9 [3] => 9 [2] => 9 [1] => 10 [6] => 38 )
Do you see the bug? No? Ok, let me explain it.
The original three '9' elments are in key order: 2,3,4. But in the result, the three '9' elements are now in key order: 4,3,2, i.e. equal-value elements are in reverse key order after sorting.
If the element is only single value, as in above example,it's fine with us. However, if the element is compound value, then it could cause human-feeling bug. See another code segments. We are to sort many points horizontally, i.e. sort them based on ascending x-coordinate value order :
function xxx($a,$b) { if ($a['x']==$b['x']) return 0; else return $a['x']<$b['x']?-1:1; }
$x=array(1=>array('x'=>1, 'v'=>'l'),2=>array('x'=>9, 'v'=>'love'),
3=>array('x'=>9, 'v'=>'Lara'),4=>array('x'=>9, 'v'=>'Croft'),
5=>array('x'=>15, 'v'=>'and'),6=>array('x'=>38, 'v'=>'Tombraider'));
uasort($x,'xxx');
print_r($x);
the result is:
Array ( [1] => Array ( [x] => 1 [v] => l ) [4] => Array ( [x] => 9 [v] => croft )
[3] => Array ( [x] => 9 [v] => Lara ) [2] => Array ( [x] => 9 [v] => love )
[5] => Array ( [x] => 15 [v] => and ) [6] => Array ( [x] => 38 [v] => Tombraider ) )
You see 'I love Lara Croft and Tombraider' becomes 'I Croft Lara love and Tombraider'.
I call it human-feeling bug because it depends on what case you use and how you feel it should be sorted in real world when the compared values are same.

Categories