George
George

Reputation: 36784

Moving array elements to end of array by sorting

If I have an array like so (it could be any combination of numbers):

$arr = array(1, 2, 4, 2, 3, 5, 4, 2, 1);

I want to move all the elements that equal 4 to the end of the array, while preserving the order of the other elements, so ideally my resulting array would be:

1, 2, 2, 3, 5, 2, 1, 4, 4

I thought I could acheve this by using a sort function:

uasort($arr, function($a, $b){
    return $b == 4 ? -1 : 1;
});

Which moves the "4" elements to the end, but ruins the order of the other elements, this is my result with the above code:

2, 3, 1, 2, 5, 2, 1, 4, 4

How should my sorting handler function look? / Is there a better way to achieve this than sorting the array?

Note; I want to preserve my array keys (hence uasort)

eval.in

Upvotes: 3

Views: 1158

Answers (4)

gmo
gmo

Reputation: 9000

Try with...

  1. count(array_keys())
  2. array_diff()
  3. array_values()
  4. array_fill()
  5. array_merge()

Example: https://eval.in/295552

CODE

<?php

// initial array
$arr = array(1, 2, 4, 2, 3, 5, 4, 2, 1);

// key to move (as a new array for array_diff)
$key_removed = array(4);

// count the number of repetitions (for array_fill)
$key_repetitions = count(array_keys($arr, $key_removed[0], true));

// new temp_array without the removed key
$temp_arr1 = array_diff($arr, $key_removed);

// reorder array index
$temp_arr1 = array_values($temp_arr1);

// add to temp_array the removed key `key_repetitions` number of times
$temp_arr2 = array_fill(count($temp_arr1) + 1, $key_repetitions, $key_removed[0]);

// merge both
$arr = array_merge($temp_arr1, $temp_arr2);

var_dump($arr);

Upvotes: 0

Tom Fenech
Tom Fenech

Reputation: 74595

Here's one way to do it with uasort:

$arr = array(1, 2, 4, 2, 3, 5, 4, 2, 1);

uasort($arr, function($a, $b){
    if ($a == 4) return -1;
    if ($b == 4) return 1;
    return 0;
});

$arr = array_reverse($arr, true);

print_r($arr);

The true argument to array_reverse preserves the keys in the array. The return 0 is optional but improves clarity in my opinion.

Output:

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

The fact that some of the keys corresponding to the same value are mixed up is unavoidable.

Upvotes: 3

Vagabond
Vagabond

Reputation: 897

How about this...??

<?php

$arr = array(1, 2, 4, 2, 3, 5, 4, 2, 1);
$temp= array();
foreach($arr as $index => $value){
  if($value == 4) {
    array_push($temp, $value);    
    unset ($arr[$index]);
  }
}
$result = array_merge($arr, $temp);
var_dump($result);

Gave me this:

array(9) {[0]=>int(1) [1]=>int(2) [2]=>int(2) [3]=>int(3) [4]=>int(5) [5]=>int(2) [6]=>int(1) [7]=>int(4)   [8]=>int(4) }

Upvotes: 1

Rizier123
Rizier123

Reputation: 59681

This should work for you:

<?php

    $arr = array(1, 2, 4, 2, 3, 5, 4, 2, 1);

    foreach($arr as $k => $v) {

        if($v == 4) {
            unset($arr[$k]);
            $arr[$k] = $v;  
        }

    }

    print_r($arr);

?>

Output:

//before 
Array ( [0] => 1 [1] => 2 [2] => 4 [3] => 2 [4] => 3 [5] => 5 [6] => 4 [7] => 2 [8] => 1 ) 
//after                    ^ key ^  value                      ^ key ^ value  
Array ( [0] => 1 [1] => 2 [3] => 2 [4] => 3 [5] => 5 [7] => 2 [8] => 1 [2] => 4 [6] => 4 )
                                                                      //^     ^  ^     ^

Upvotes: 4

Related Questions