array_diff()在存在重复数组值时进行“一对一”元素删除。

8
我有两个包含重复值的数组:
$test1 = [
    "blah1",
    "blah1",
    "blah1",
    "blah1",
    "blah2"
];

$test2 = [
    "blah1",
    "blah1",
    "blah1",
    "blah2"
];

我正在尝试获取数组差异:

$result = array_diff($test1,$test2);

echo "<pre>";
print_r($result);

我需要它返回只有一个值的数组 blah1,但实际上它返回了空数组。

我怀疑这与两个数组中有重复值有关,但不确定如何修复。


1
你的解决方案很好,但如果你有$array1 = [ 'a', 'b', 'c' ]$array2 = [ 'd' ]时它会失败。输出结果应该与$array1相同,但实际上是[ 'b', 'c' ],因为当查找darray_search()将返回false,并且unset()将删除$array1中的第一个键,因为false == 0。在此情况下,可以使用if语句解决此问题(gistrun)。 - David Rodrigues
@Acidon,我认为你应该把自己的解决方案作为答案添加进去,因为我没有找到更好的方法来解决它。 - Jules Colle
3个回答

4
array_diff函数将第一个array与作为参数传递的其他array(s)进行比较,并返回一个包含第一个array中存在但其他arrays中不存在的所有元素的array。由于$test1$test2都包含"blah1""blah2",并且没有其他值,因此,array_diff的预期行为就是您所经历的那样,即返回一个空的array,因为在$test1中没有任何元素不在$test2中。请参考进一步阅读。此外,请阅读一些理论以了解您正在处理的内容。

2
我现在明白了,但是我应该使用什么来获得所需的效果呢? - Acidon
我猜我可以循环遍历test1并在两个数组中删除每个匹配的值,但是我正在寻找更优雅的解决方案。 - Acidon
@Acidon,如果第一个数组中有5个"blah1"而没有"blah2",且第二个数组不变,那么结果应该是什么? - Lajos Arpad
编辑中的函数会得到结果,它应该是"blah1","blah2"。 - Acidon
@Acidon,我明白了。现在我理解你所询问的问题 :) - Lajos Arpad

2

发现了Acidon自己的解决方案存在问题。问题在于unset($array[false])实际上会取消设置$array[0],因此需要明确检查false(正如David Rodrigues也指出的那样)。

function subtract_array($array1,$array2){
    foreach ($array2 as $item) {
        $key = array_search($item, $array1);
        if ( $key !== false ) {
            unset($array1[$key]);
        }
    }
    return array_values($array1);
}

一些例子

subtract_array([1,1,1,2,3],[1,2]);            // [1,1,3]
subtract_array([1,2,3],[4,5,6]);              // [1,2,3]
subtract_array([1,2,1],[1,1,2]);              // []
subtract_array([1,2,3],[]);                   // [1,2,3]
subtract_array([],[1,1]);                     // []
subtract_array(['hi','bye'], ['bye', 'bye']); // ['hi']

0
根据您的任务范围,可能需要仅从第一个数组中删除在第二个数组中以“一对一”方式表示的元素。在其他情况下,可能需要以“一对一”的方式交叉检查两个数组的差异,并合并剩余的元素。
考虑这个修改后的样本数据集:
$test1 = [
    "blah1",
    "blah1",
    "blah2",
    "blah4",
    "blah5"
];

$test2 = [
    "blah1", // under-represented
    "blah2", // equally found
    "blah3", // not found
    "blah4", // over-represented
    "blah4", //       "
];

以下是四个不同的功能(带有指示性名称),以提供各种实用性。
代码:(演示
  • 单边差异(迭代数组搜索):

    function removeBValuesFromA(array $a, array $b): array
    {
        foreach ($b as $bVal) {
            $k = array_search($bVal, $a);
            if ($k !== false) {
                unset($a[$k]);
            }
        }
        return array_values($a);
    }
    
  • 双边差异(迭代数组搜索):

    function bidirectionalDiff(array $a, array $b): array
    {
        foreach ($b as $bKey => $bVal) {
            $aKey = array_search($bVal, $a);
            if ($aKey !== false) {
                unset($a[$aKey], $b[$bKey]);
            }
        }
        return array_merge($a, $b);
    }
    
  • 单边差异(压缩-比较-展开):

    function removeBValuesFromAViaCounts(array $a, array $b): array
    {
        $toRemove = array_count_values($b);
    
        $result = [];
        foreach (array_count_values($a) as $k => $count) {
            array_push(
                $result,
                ...array_fill(
                    0,
                    max(0, $count - ($toRemove[$k] ?? 0)),
                    $k
                )
            );
        }
        return $result;
    }
    
  • 双边差异(压缩-比较-展开):

    function bidirectionalDiffViaCounts(array $a, array $b): array
    {
        $bCounts = array_count_values($b);
    
        $result = [];
        foreach (array_count_values($a) as $k => $count) {
            array_push(
                $result,
                ...array_fill(
                    0,
                    abs($count - ($bCounts[$k] ?? 0)),
                    $k
                )
            );
            unset($bCounts[$k]);
        }
        foreach ($bCounts as $k => $count) {
            array_push(
                $result,
                ...array_fill(0, $count, $k)
            );
        }
        return $result;
    }
    
执行:
var_export([
    'removeBValuesFromA' => removeBValuesFromA($test1, $test2),
    'bidirectionalDiff' => bidirectionalDiff($test1, $test2),
    'removeBValuesFromAViaCounts' => removeBValuesFromAViaCounts($test1, $test2),
    'bidirectionalDiffViaCounts' => bidirectionalDiffViaCounts($test1, $test2),
]);

输出:

array (
  'removeBValuesFromA' => 
  array (
    0 => 'blah1',
    1 => 'blah5',
  ),
  'bidirectionalDiff' => 
  array (
    0 => 'blah1',
    1 => 'blah5',
    2 => 'blah3',
    3 => 'blah4',
  ),
  'removeBValuesFromAViaCounts' => 
  array (
    0 => 'blah1',
    1 => 'blah5',
  ),
  'bidirectionalDiffViaCounts' => 
  array (
    0 => 'blah1',
    1 => 'blah4',
    2 => 'blah5',
    3 => 'blah3',
  ),
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接