1

I am using asort to sort the numeric array. For e.g.

$arr = [0,1,1,2,1,2,2,3];

After running asort I am getting:

Array
(
    [0] => 0
    [4] => 1
    [2] => 1
    [1] => 1
    [6] => 2
    [3] => 2
    [5] => 2
    [7] => 3
)

But I am expecting to get it in this order:

Array
(
    [0] => 0
    [1] => 1
    [2] => 1
    [4] => 1
    [3] => 2
    [5] => 2
    [6] => 2
    [7] => 3
)

See the difference in order of the keys above.

Rizier123
  • 58,877
  • 16
  • 101
  • 156
codelearner
  • 1,354
  • 1
  • 16
  • 32
  • 2
    Quoting from the [PHP Docs](http://www.php.net/manual/en/array.sorting.php): `If any of these sort functions evaluates two members as equal then the order is undefined (the sorting is not stable).` So you shouldn't expect key sequence to be maintained for elements with the same value, this is the documented behaviour – Mark Baker Aug 06 '15 at 10:44
  • @MarkBaker I want to make it's order defined and sorting as stable. :) – codelearner Aug 06 '15 at 10:46
  • 2
    Then you'll need to write your own sort function to do so: PHP doesn't have a function that will do it for you.... [this answer](http://stackoverflow.com/questions/12676521/how-to-have-a-stable-sort-in-php-with-arsort) might help – Mark Baker Aug 06 '15 at 10:56

2 Answers2

3

First sort the array. Then generate an array by flipping in a way so that the keys can be separated according to values. Sort the arrays with keys and merge them to an array. And the combine the keys with the sorted values.

$arr = [0,1,1,2,1,2,2,3];

asort($arr);
$sorted = $arr;

$flipped = $new_keys = array();
foreach($arr as $key => $val) {
   $flipped[$val][] = $key; // Get the keys
}

foreach($flipped as $key => $val_array) {
    asort($val_array); // Sort the keys
    $new_keys = array_merge($new_keys, $val_array);
}

$final = array_combine($new_keys, $sorted); // Combine them again
var_dump($final);

Output

array(8) {
  [0]=>
  int(0)
  [1]=>
  int(1)
  [2]=>
  int(1)
  [4]=>
  int(1)
  [3]=>
  int(2)
  [5]=>
  int(2)
  [6]=>
  int(2)
  [7]=>
  int(3)
}
Sougata Bose
  • 31,517
  • 8
  • 49
  • 87
3

This should work for you:

First walk through each array value with array_walk() and change each value to an array containing the value and the key.

After this use uasort() to sort your array and if both values are the same you use the key to choose which one should be first.

At the end just use array_column() to transform your array back.

<?php

    $arr = [0,1,1,2,1,2,2,3];
    array_walk($arr, function(&$v, $k){
        $v = ["value" => $v, "key" => $k];
    });

    uasort($arr, function($a, $b){
        if($a["value"] == $b["value"]) {
            if($a["key"] == $b["key"])
                return 0;
            return $a["key"] > $b["key"] ? 1 : -1;
        }
        return $a["value"] > $b["value"] ? 1 : -1;
    });
    $arr = array_column($arr, "value", "key");

    print_r($arr);

?>

output:

Array
(
    [0] => 0
    [1] => 1
    [2] => 1
    [4] => 1
    [3] => 2
    [5] => 2
    [6] => 2
    [7] => 3
)
Rizier123
  • 58,877
  • 16
  • 101
  • 156
  • Humm... yup... but there are now 2 solutions on this question now, one by you and another by @b0s3 . Just curious to know which one should be less time consuming. – codelearner Aug 06 '15 at 11:23
  • 1
    @codelearner Look at it for yourself: http://3v4l.org/aSPXJ/perf#tabs ; http://3v4l.org/3Ifc7/perf#tabs and http://3v4l.org/DdXGf/perf#tabs – Rizier123 Aug 06 '15 at 11:42
  • 1
    @codelearner Also just to note this, if you look at the results above, make sure to look only at PHP 5.5+ for my answer, since under that version mine does not work. – Rizier123 Aug 06 '15 at 11:48
  • I have voted your solution but based on the time consumption I have to accept @b0s3 solution. Thanks for your time though. – codelearner Aug 06 '15 at 11:59