Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding array sorting behavier php

I have this array

$array = array(2, 1, "img1", "img2", "img10", 1.5, "3.14", "2.72");

after applying sort function it becomes

sort($array);

Array
(
    [0] => 2.72
    [1] => 3.14
    [2] => img1
    [3] => img10
    [4] => img2
    [5] => 1
    [6] => 1.5
    [7] => 2
)

I did't understand how the sorting is performed.

Can anyone explain how the output array is achieved?

EDIT:

Here the question is not that which SORT FLAG should I use but the question is how the above sorting is performed. Any advise to use another SORT FLAG is useless here.

like image 738
Daric Avatar asked Feb 07 '13 14:02

Daric


3 Answers

The first two numbers were actual strings (you passed them within quotes), so they are treated like so: numbers come before any letter in alphabetic sorting.

The other ones were real numbers (without quotes), so the function put them separately, sorting them numerically.

array(8)
    0 => '2.72'    [alphabetical]
    1 => '3.14'    [alphabetical]
    2 => 'img1'    [alphabetical]
    3 => 'img10'   [alphabetical]
    4 => 'img2'    [alphabetical]
    5 => 1         [numeric]
    6 => 1.5       [numeric]
    7 => 2         [numeric]

So, the function has to decide if it will sort the elements alphabetically (a,b,c...) or numerically (1,2,3..) so it just checks the variable type.

Like Pekka pointed out, there are flags that can be set to force the type of sorting. They are described in the docs.


EDIT

This theory was proved wrong in the comments and I'm totally lost now (:

This comment at the docs contains some interesting points on this issue.

like image 93
Hugo Mota Avatar answered Oct 13 '22 04:10

Hugo Mota


Using the information in the link for Coanda as a starting point, it is clear that PHP using type juggling when comparing objects of different types. The problem is understanding what is getting casted to what during comparison, and I still havn't been able to find a complete list, but in general:

string compared to int will turn into

(int) string <,>,=, (int) int 

In this case the string, after casting becomes 0, so all ints are greater than a string. (expected). This is the only case we need to worry about for this sort.

PHP uses quicksort, and most likely chooses its pivot point as array[n/2] where n is the number elements in the array. Knowing these two pieces of information we quicksort the above array:

$pivot = $array[n/2] //n is the number of elements, this sets $pivot='img2'
//compare each element in the list (i am going to this by hand for demonstration)

(int) 'img2' < 2 //int to int comparison;'img2' resolves to 0 and 0 < 2
(int) 'img2' < 1 //int to int comparison;'img2' resolves to 0 and 0 < 1
      'img2' > 'img1' // string to string comparison; strcmp is +256 
      'img2' > 'img 10' //string to string comparison; strcmp is +256 
(float) 'img2' < 1.5 //float to float comparison;'img2' resolves to 0 and 0<1.5 
      'img2' > '3.14' //string to string comparison; strcmp is +54
      'img2' > '2.72' //string to string comparison; strcmp is +55

We now have to two new arrays (one for greater and one for less than).

 $greater = array('img1', 'img10', '3.14', '2.72);
 $less = array(2, 1, 1.5);

Now there is little need to go into further detail as we have accidentally made 2 arrays that have all easily comparable objects in it. $greater has only strings, and we can assume sort will work normally here and considering everything a string.

sort($greater);
var_dump($greater);

produces

array(4) {
  [0]=>
  string(5) "2.72"
  [1]=>
  string(4) "3.14"
  [2]=>
  string(4) "img1"
  [3]=>
  string(5) "img10"
}

This is what we expected and also the result above. We do the same with $lesser

$lesser = array(2, 1, 1.5);
sort($lesser);
var_dump($lesser);

we get

array(3) {
  [0]=>
  int(1)
  [1]=>
  float(1.5)
  [2]=>
  int(2)
}

Which is also expected. Now when we concat all three arrays together (for the sake of recursiveness I am calling 'img2' an array). We get the results above.

Array
(
[0] => 2.72
[1] => 3.14
[2] => img1
[3] => img10
[4] => img2
[5] => 1
[6] => 1.5
[7] => 2
)

To prove this point, you can follow the same process for this same array, but switch out the $arr[3] with an integer.

$arr = array("img2", 1, "img1", 2, "img10", 1.5, "3.14", "2.72");
sort($arr);
var_dump($arr)

gives you a totally different result because the pivot changed from a string to an int causing the float strings to evaluate as floats.

array(8) {
[0]=>
string(4) "img1"
[1]=>
string(5) "img10"
[2]=>
string(4) "img2"
[3]=>
int(1)
[4]=>
float(1.5)
[5]=>
int(2)
[6]=>
string(4) "2.72"
[7]=>
string(4) "3.14"
}
like image 26
Scott Avatar answered Oct 13 '22 05:10

Scott


The following bug gives more insight in sorting mixed types: https://bugs.php.net/bug.php?id=21728

Quote:

<?php
$arr1 = array("a","b","c","d","4",5,4,"true","TRUE",true);
sort($arr1);
var_dump($arr1);
?>

The output is :
array(10) {
  [0]=>
  bool(true)
  [1]=>
  int(4)
  [2]=>
  string(1) "4"
  [3]=>
  string(4) "TRUE"
  [4]=>
  string(1) "a"
  [5]=>
  string(1) "b"
  [6]=>
  string(1) "c"
  [7]=>
  string(1) "d"
  [8]=>
  string(4) "true"
  [9]=>
  int(5)
}

It may look strange - why (int)5 is after all the strings. This is because "4" is lower than (int) 5, "4" is before "true" and "true" is before 5. The first 2 are obvious, the third one is not. But it is ok. It's better not to mix types in the array. If 5 is changed to "5" then "5" goes right after "4".

like image 34
Coanda Avatar answered Oct 13 '22 04:10

Coanda