Home > database >  Compare two arrays and present intersections/differences in a github-style
Compare two arrays and present intersections/differences in a github-style

Time:10-16

I have two arrays which are sorted alphabetically. Each array contains unique values, but some values will be shared between the teo arrays.

Sample arrays:

$src = ["apple", "cherry", "grape", "lemon", "orange", "strawberry"];
$dst = ["apple", "banana", "cherry", "orange", "pear"];

I'd like to output two lists as they were a file comparison or "diff checker" in github/style, like this:

1.apple         1.apple
2.              2.banana
3.cherry        3.cherry
4.grape         4.
5.lemon         5.
6.orange        6.orange
7.              7.pear
8.strawberry    8.

I'm stuck in finding the right way to do this. Okay, I do a foreach loop for both arrays, but then what?

<ul>
<?php foreach($src as $src_item) : ?>
    <li><?php echo $src_item; // what else??? ?></<li>
<?php endforeach; ?>
</ul>

CodePudding user response:

You could simply combine the arrays unique items (then re-sort it!) Then you can just loop the new array and check which items are in the original 2 arrays.

<?php

$src = ["apple", "cherry", "grape", "lemon", "orange", "strawberry"];
$dst = ["apple", "banana", "cherry", "orange", "pear"];

$combined_items = array_unique(array_merge($src, $dst));
sort($combined_items);

$new_array = [];

foreach($combined_items as $item){
    $item1 = in_array($item, $src) ? $item : null;
    $item2 = in_array($item, $dst) ? $item : null;
    $new_array[] = [$item1, $item2];
}

// $new_array will contain ["apple","apple"], [null, "banana"], etc.

CodePudding user response:

For cases where the input arrays are not guaranteed to be alphabetically ordered and may contain duplicates, I've come up with the following script.

  1. Loop until at least one of the arrays is exhausted/consumed. It you want to use these original arrays later, make a copy or wrap my snippet in a function call.
  2. If both values are identical, push them into the same row in the result array.
  3. If not identical, determine which arrays has the closer matching value, and push all values before that match as individual rows into the result array.
  4. When the loop finishes, make sure to push all remaining values into the result array.

Code: (Demo)

$src = ["apple", "cherry", "grape", "lemon", "orange", "strawberry", "cherry", "cherry"];
$dst = ["apple", "banana", "cherry", "orange", "pear", "cherry"];

$result = [];
while ($src && $dst) {
    if ($src[0] === $dst[0]) { // align identical values
        $result[] = [array_shift($src), array_shift($dst)];
        continue;
    }

    $earliestSrcMatch = array_search($src[0], $dst);
    $earliestDstMatch = array_search($dst[0], $src);

    if ($earliestSrcMatch === $earliestDstMatch) { //both false (not 0)
        $result[] = [array_shift($src), null];
        $result[] = [null, array_shift($dst)];
        continue;
    } elseif (
        $earliestDstMatch === false
        || (
            $earliestSrcMatch !== false
            && $earliestSrcMatch < $earliestDstMatch
        )
    ) {
        while ($dst && $src[0] !== $dst[0]) {
            $result[] = [null, array_shift($dst)];
        }
    } elseif (
        $earliestSrcMatch === false
        || $earliestSrcMatch > $earliestDstMatch
    ) {
        while ($src && $src[0] !== $dst[0]) {
            $result[] = [array_shift($src), null];
        }
    }
}
while ($src) {
    $result[] = [array_shift($src), null];
}
while ($dst) {
    $result[] = [null, array_shift($dst)];
}

To visualize as a table:

echo "<table border=1>
";
foreach ($result as $i => [$s, $d]) {
    printf(
        "<tr>
             <td>%d</td>
             <td>%s</td>
             <td>%s</td>
         </tr>\n",
           $i,
         $s,
         $d
    );
}
echo "</table>";

My take on @Snor's approach would be to set up two lookup arrays so that the performance benefits of key sorting and searching can be enjoyed. On relatively large arrays, iterated calls of in_array() may place unwanted drag on performance.

Code: (Demo)

$src = array_combine($src, $src);
$dst = array_combine($dst, $dst);
$combined_items = $src   $dst;
ksort($combined_items);

$result = [];
foreach ($combined_items as $item) {
    $result[] = [
        $src[$item] ?? null,
        $dst[$item] ?? null
    ];
}
  • Related