1

Using PHP I have this array:

[0]: John
[1]: Brian
[2]: Julia
[3]: Adam
[4]: Brian
[5]: Jonathan
[6]: Amanda
[7]: Julia
[8]: Nathan

I want to sort the array so that the order is as close to the original as possible, but with duplicates stacked, creating the following array:

[0]: John
[1]: Brian
[2]: Brian (duplicate value moved from index 4)
[3]: Julia
[4]: Julia (duplicate value moved from index 7)
[5]: Adam
[6]: Jonathan
[7]: Amanda
[8]: Nathan

I'm assuming that it's a matter of nested foreach loops, but I'm unsure how to apply unset() in a nested foreach loop.

EDIT: Since i probably didn't explain it very well I want to keep the duplicates in the array. I do not want to remove the duplicates.

6
  • Do you want to remove the duplicate or leave it? Commented May 24, 2016 at 20:04
  • Could you also post the code that you're currently having? Commented May 24, 2016 at 20:04
  • Duplicate it. I don't want to delete the duplicated values, just stack them together. Commented May 24, 2016 at 20:04
  • So really, you are not even sorting. What you are doing is moving the second and later duplicates to right after the first found value. Commented May 24, 2016 at 20:11
  • Why is the last Brian left alone and not moved? Commented May 24, 2016 at 20:11

6 Answers 6

1

The solution using array_shift, array_intersect and in_array functions:

$arr = [0=> 'John',1=> 'Brian',2=> 'Julia',3=> 'Adam',4=> 'Brian',5=> 'Jonathan',6=> 'Amanda',7=> 'Julia',8=> 'Nathan'];

$size = count($arr);
$i = 0;
$result = [];
while ($i < $size) {
    $el = array_shift($arr);  // current value
    if (!in_array($el, $result)) $result[] = $el;
    $dups = array_intersect($arr, [end($result)]);  // finding duplicates
    if (count($dups)) $result = $result + $dups;    // adding duplicates to "stack" if exist
    $i++;
}

print_r($result);

The output:

Array
(
    [0] => John
    [1] => Brian
    [2] => Brian
    [3] => Julia
    [4] => Julia
    [5] => Adam
    [6] => Jonathan
    [7] => Amanda
    [8] => Nathan
)
Sign up to request clarification or add additional context in comments.

Comments

1

Not the most efficient solution, but works:

function dupsort(array $input)
{
    $output = array();
    $moved = array();
    foreach ($input as $key => $val)
    {
        if (isset($moved[$key])) {
            continue;
        }

        $moved[$key] = true;
        $output[] = $val;

        foreach ($input as $dupKey => $dupVal) {

            if ($dupVal!==$val) {
                continue;
            }

            if (isset($moved[$dupKey])) {
                continue;
            }

            $moved[$dupKey] = true;
            $output[] = $dupVal;
        }
    }

    return $output;
}

Comments

1

It works, just tested.

for ($i = 0; $i < count($array); $i++) {
    for ($j = $i; $j < count($array); $j++) {

        if ($i < $j && $array[$i] == $array[$j]) {
            $insert = array($array[$j]);
            unset($array[$j]);
            array_splice($array,$i,0,$insert);
        }
    }
}

Comments

1

Allow me to offer a slightly more resource friendly solution:

  function dupsort($data) {
    $result = [];

    foreach($data as $word) {

        // remove all matches of the current word from the source
        $before = count($data);
        $data = array_filter($data, function($x) use ($word) {
            return $x !== $word;
        });

        // add the word to the output as many times as it got removed from source
        $newCount = count($result) + $before - count($data);
        $result = array_pad($result, $newCount, $word);
    }

    return $result;
  }

Comments

0

Just use the sort function.

$a = array("a", "b", "c", "a");

sort($a);
var_dump($a);

Result is:

array(4) { [0]=> string(1) "a" [1]=> string(1) "a" [2]=> string(1) "b" [3]=> string(1) "c" }

3 Comments

I thought this at first but it's not quite what OP is looking for
Are you saying OP wants those "(duplicate value moved from index 4)" as part of the result?
@FredericoSchardong Yes! That is exactly what I want as part of the result
0

Use just one loop to create a reduced lookup map of the first occurrences of each unique value as well as an ordering array which will hold the earliest index of each value.

Then call array_multisort() to sort the original array by the ordering array.

No iterated function calls are written.

Code: (Demo)

$order = [];
foreach ($array as $i => $v) {
    $lookup[$v] ??= $i;
    $order[] = $lookup[$v];
}
array_multisort($order, $array);
var_export($array);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.