Merge two flat arrays and omit values from one array when the other array has more occurrences of the same value

103 Views Asked by At

I want to merge every element of two arrays, BUT if a value is in both arrays, then only add the values from the array which has the biggest amount of that element. The result array does not need to be sorted in any special way, but I did it here for readability.

Sample input:

$array1 = [1, 4, 7, 3, 3, 3];
$array2 = [4, 0, 3, 4, 9, 9];

Desired result:

 [0, 1, 3, 3, 3, 4, 4, 7, 9, 9]
//a2 a1 a1 a1 a1 a2 a2 a1 a2 a2

Note, this will be used on big arrays, with unknown integer values. Is there a good way to do this that doesn't require too much time/processing power?

4

There are 4 best solutions below

0
chiliNUT On

probably not the most optimized but

<?php
$one=[1, 4, 7, 3, 3, 3];
$two=[4, 0, 3, 4, 9, 9];
sort($one);
sort($two);
foreach($one as $el)
{
$combined[]=$el;
if (array_search($el,$two))
{
unset($two[array_search($el,$two)]);
}
}
foreach($two as $el)
{
$combined[]=$el;
}
sort($combined);
print_r($combined);
?>
2
Dave Chen On

Try this:

<?php

$array1 = [1, 4, 7, 3, 3, 3];
$array2 = [4, 0, 3, 4, 9, 9];

function min_merge($arr1, $arr2) {
    $arr1 = array_count_values($arr1);
    $arr2 = array_count_values($arr2);

    foreach ($arr2 as $index => $arr)
        if (!isset($arr1[$index]) || $arr > $arr1[$index])
            $arr1[$index] = $arr;

    foreach ($arr1 as $index => $arr)
        for ($i = 0; $i < $arr; $i++)
            $final[] = $index;

    return $final;
}

print_r(min_merge($array1, $array2));

Output:

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

Unsorted, but it contains all the numbers from [0, 1, 3, 3, 3, 4, 4, 7, 9, 9].

0
Expedito On
$count[0] = array_count_values($arr1);
$count[1] = array_count_values($arr2);
$out = array();
array_map(function($e) use(&$out, $count){
    $n1 = (isset($count[0][$e])) ? $count[0][$e] : 0;
    $n2 = (isset($count[1][$e])) ? $count[1][$e] : 0;
    $next = ($n2 > $n1) ? array_fill(0, $n2, $e) : array_fill(0, $n1, $e);
    $out = array_merge($out, $next);
}, array_keys($count[0] + $count[1]));
print_r($out);
0
mickmackusa On
  1. My modernized rewrite of @DaveChen's answer using PSR-12 coding standards and eliminating single-use declarations. This approach uses one loop to determine the greater count for numbers shared by both value-count arrays, then a second loop to populate the result array. (Demo)

    $counts1 = array_count_values($array1);
    foreach (array_count_values($array2) as $number => $count) {
        if ($count > ($counts1[$number] ?? 0)) {
            $counts1[$number] = $count;
        }
    }
    $result = [];
    foreach ($counts1 as $number => $count) {
        array_push($result, ...array_fill(0, $count, $number));
    }
    var_export($result);
    
  2. My modernized rewrite of @Expedito's answer which does not abuse the array_map() (when array_map()'s return value is not used, use array_walk() for functional style programming), uses a foreach() loop to eliminate variable scope issues, and generally implements D.R.Y. techniques. (Demo)

    $counts1 = array_count_values($array1);
    $counts2 = array_count_values($array2);
    $result = [];
    foreach ($counts1 + $counts2 as $num => $cnt) { 
        array_push(
            $result,
            ...array_fill(
                0,
                max($counts1[$num] ?? 0, $counts2[$num] ?? 0),
                $num
            )
        );    
    }
    var_export($result);
    
  3. And I wanted to add a new approach of my own, despite the fact that it may or may not perform better than the other two snippets. The script makes one pass over the first value count arrays to populate a temporary array which demands which numbers from the first array should be represented in the result array. Then it isolates value intersections from the first array, value differences from the second array, then merges them. (Demo)

    $counts1 = array_count_values($array1);
    $counts2 = array_count_values($array2);
    $keepFrom1 = array_keys(
        array_filter(
            $counts1,
            fn($count, $number) => ($counts2[$number] ?? 0) <= $count,
            ARRAY_FILTER_USE_BOTH
        )
    );
    
    var_export(
        array_merge(
            array_intersect($array1, $keepFrom1),
            array_diff($array2, $keepFrom1)
        )
    );