Home > front end >  Merging two arrays by conditionally toggling which array's row should be used next
Merging two arrays by conditionally toggling which array's row should be used next

Time:08-22

I've got two arrays with the same objects. They are of the form:

$arrayA = [
    ["sentence" => "Hello world",        "nextSpeaker" => 0],
    ["sentence" => "Hello world again",  "nextSpeaker" => 1],
    ["sentence" => "Hello world twice!", "nextSpeaker" => 1],
];

$arrayB = [
    ["sentence" => "Bye world",       "nextSpeaker" => 1],
    ["sentence" => "Bye world again", "nextSpeaker" => 0],
    ["sentence" => "Bye world twice", "nextSpeaker" => 0],
];

The effect that I want to achieve is, starting with Array A, to merge both arrays but have then in the correct order. So, if from array A, the nextSpeaker is 0, it should take the next result from ArrayA. If the nextSpeaker is 1, then it should take from ArrayB and then look at the next item from ArrayB to see if the next speaker is speaker 1. If not, it shuold look at ArrayA again.

I have tried to just merge both arrays, loop through the merged array and keeping track of the current key for the speaker0 and speaker1 arrays, but I can't really wrap my head around it.

Desired output:

[
    ['sentence' => 'Hello world',        'nextSpeaker' => 0],
    ['sentence' => 'Hello world again',  'nextSpeaker' => 1],
    ['sentence' => 'Bye world',          'nextSpeaker' => 1],
    ['sentence' => 'Hello world twice!', 'nextSpeaker' => 1],
    ['sentence' => 'Bye world again',    'nextSpeaker' => 0],
    ['sentence' => 'Bye world twice',    'nextSpeaker' => 0],
]

CodePudding user response:

This can be done by using a while loop until both arrays are empty. Foreach over the first array with shifting first value off as long as the break condition matches, do the same for the seconds array.

Warning! This mutates and clears $arrayA and $arrayB. If you don't want it, make a copy first.

$arrayA = [
    ["sentence" => "Hello world", "nextSpeaker" => 0],
    ["sentence" => "Hello world again", "nextSpeaker" => 1],
    ["sentence" => "Hello world twice!", "nextSpeaker" => 1],
];

$arrayB = [
    ["sentence" => "Bye world", "nextSpeaker" => 1],
    ["sentence" => "Bye world again", "nextSpeaker" => 0],
    ["sentence" => "Bye world twice", "nextSpeaker" => 0],
];

$arrayC = [];

while ($arrayA || $arrayB) {
    foreach ($arrayA as $array) {
        $arrayC[] = array_shift($arrayA);
        if ($array['nextSpeaker'] == 1) {
            break;
        }
    }

    foreach ($arrayB as $array) {
        $arrayC[] = array_shift($arrayB);
        if ($array['nextSpeaker'] == 1) {
            break;
        }
    }
}

results in

[
    ['sentence' => 'Hello world',        'nextSpeaker' => 0],
    ['sentence' => 'Hello world again',  'nextSpeaker' => 1],
    ['sentence' => 'Bye world',          'nextSpeaker' => 1],
    ['sentence' => 'Hello world twice!', 'nextSpeaker' => 1],
    ['sentence' => 'Bye world again',    'nextSpeaker' => 0],
    ['sentence' => 'Bye world twice',    'nextSpeaker' => 0],
]

CodePudding user response:

You can take advantage of the PHP arrays iterators:

reset($arrayA);
reset($arrayB);
$arrays = [$arrayA, $arrayB];
$index = 0;
$merged = [];
while ($currentElement = current($arrays[$index])) {
    $merged []= $currentElement;
    next($arrays[$index]);
    $index = $currentElement['nextSpeaker'];
    $currentArray = $arrays[$index];
}

reset might not be needed, if the arrays iterators were not changed.

If you don't like the while ($currentElement = current($arrays[$index])), you can do this instead:

$arrays = [$arrayA, $arrayB];
$index = 0;
$merged = [];
for (;;) {
    $currentElement = current($arrays[$index]);
    if (!$currentElement) {
        break;
    }
    $merged []= $currentElement;
    next($arrays[$index]);
    $index = $currentElement['nextSpeaker'];
    $currentArray = $arrays[$index];
}

If you don't trust the for(;;) loop, you can do:

$length = count($arrayA)   count($arrayB);
for ($i = 0; $i < $length;   $i) {

CodePudding user response:

I find it most elegant to declare the two inputs into one master array, then conditionally toggle which array is to be access while shifting rows into the result.

When the loop is broken because one of the input arrays has become empty/exhausted, then merge any remaining rows from the other array into the result.

Code: (Demo)

$arrays = [$arrayA, $arrayB];
$pullFrom = 0;
$result = [];
while ($arrays[0] && $arrays[1]) {
    $result[] = $row = array_shift($arrays[$pullFrom]);
    $pullFrom = $row['nextSpeaker'] ? 1 - $pullFrom : $pullFrom;
}

var_export(array_merge($result, ...$arrays));
  • Related