Search code examples
phparrayssortingmerge

Randomly interleave/zipper flat arrays without losing element order from original arrays


I want to merge / mix two arrays together, but I want it to be randomly "mixed" however NOT shuffled. For example:

$first = [1, 2, 3, 4];
$second = [10, 20, 30, 40];

Possible desired "mixes" are:

  • [1, 10, 20, 30, 2, 40, 3, 4]
  • [10, 1, 2, 20, 30, 3, 4, 40]
  • [1, 2, 3, 10, 20, 4, 30, 40]

Note that if we pluck the values back out we would still have the original orders of:

1, 2, 3, 4
10, 20, 30, 40

Solution

  • Well, there's (yet another) one possible approach:

    $arr = call_user_func_array('array_merge', array_map(null, $first, $second));
    print_r($arr); // [1, 10, 2, 20, 3, 30, 4, 40];
    

    Demo. That's apparently deterministic; for random ordering each pair should be shuffled additionally. For example:

    function zipShuffle($first, $second) {
      return call_user_func_array('array_merge', array_map(function($a, $b) {
        return mt_rand(0, 1) ? [$a, $b] : [$b, $a];
      }, $first, $second));
    }
    

    ... but that obviously won't be able to churn out something like [1,2,3,10,20...]. If this is required, here's another solution:

    function orderedShuffle($first, $second) {
      $results = [];
      $stor    = [$first, $second];
      $i       = mt_rand(0, 1);
      // switching between arrays on the fly
      while (list(, $v) = each($stor[$i])) {
        $results[] = $v;
        $i = mt_rand(0, 1);
      }
    
      // one array is gone, now we need to add all the elements
      // of the other one (as its counter left where it was)
      $i = 1 - $i;
      while (list(, $v) = each($stor[$i])) {
        $results[] = $v;
      }
      return $results;
    }
    

    Demo. The last function is actually quite easy to extend for as many arrays as required.