查找关联数组之间双向差异的最有效方法

Most efficient way to find bidirectional differences between associative arrays

我有一个联系人数组,其中包含我需要更新的一组值。我还有一个 contactChangeLog 数组,用于保存现有联系人和更新联系人之间的差异。只有更新的密钥需要保存到日志中。所以有 2 个联系人数组:

$oldContact = array(
        'name' => 'Joeahkd',
        'address' => '123 Masjdhfin',
        'city' => 'Spring',
        'state' => 'CA',
        'zip' => '90101',
 );

$newContact = array(
        'name' => 'Joe',
        'address' => '123 Main St',
        'city' => 'Springville',
        'state' => 'CA',
        'zip' => '90101',
);

我可以使用 array_diff_assoc()...

$existing = array_diff_assoc($oldContact, $newContact);
$update = array_diff_assoc($newContact, $oldContact);

$diff = array('previous' => $existing, 'new' => $update);
print_r($diff);

但是,我对每个数组进行了两次迭代,我了解到的大 O 表示法告诉我这是不对的。是否有一种算法更有效的方法来返回更新后的键以及现有值和更新后的值?结果应该是:

$previous = array(
        'name' => 'Joeahkd',
        'address' => '123 Masjdhfin',
        'city' => 'Spring'
 );

$updated = array(
        'name' => 'Joe',
        'address' => '123 Main St',
        'city' => 'Springville'
);

我将尝试的下一个方法是:

public function diff($newObj, $oldObj)
{
    $updatedVals = [];
    $previousVals = [];

    foreach ($newObj as $key => $value)
    {
        if ($newObj[$key] !== $oldObj[$key])
        {
                $updatedVals[] = $newObj[$key];
                $previousVals[] = $oldObj[$key];
        }

        return [$newVals, $previousVals];
    }
}

你可以试试array_filter:

$updated = array();    
$previous = array_filter($oldContact, function($v, $k) {
    if ($v != $newContact[$k]) {
        $updated[$k] = $newContact[$k];
        return true;
    }
    return false;
}, ARRAY_FILTER_USE_BOTH);

array_filter 将 return 来自输入数组的 keys/values 如果回调函数我们在每次 key/value 迭代期间指定 returns true 并且那些将成为我们的 $previous。当我们在回调函数中进行检查时,我们构造 $updated.

更新:

array_filterarray_intersect_key 结合使用甚至可以进一步削减代码:

$previous = array_filter($oldContact, function($v, $k) {
    return ($v != $newContact[$k]);
}, ARRAY_FILTER_USE_BOTH);

$updated = array_intersect_key($newContact, $previous);

我几乎同意@shotdsheriff 的方法,但它有一个拼写错误,它 returns 破坏关联关系后的两个数组,重要的是要注意潜在的边缘情况,其中一个键在一个数组中找到,但在另一个数组中找不到。

代码:(Demo)

$oldContact = array(
        'name' => 'Joeahkd',
        'address' => '123 Masjdhfin',
        'city' => 'Spring',
        'a-key-only-in-old' => 'foo',
        'state' => 'CA',
        'zip' => '90101',
 );

$newContact = array(
        'name' => 'Joe',
        'a-key-only-in-new' => 'bar',
        'address' => '123 Main St',
        'city' => 'Springville',
        'state' => 'CA',
        'zip' => '90101',
);

$oldDiff = [];
$newDiff = [];
foreach ($oldContact + $newContact as $key => $value) {
    if (isset($oldContact[$key]) && !isset($newContact[$key])) {
        $oldDiff[$key] = $oldContact[$key];
    } elseif (isset($newContact[$key]) && !isset($oldContact[$key])) {
        $newDiff[$key] = $newContact[$key];
    } elseif ($oldContact[$key] !== $newContact[$key]) {
        $oldDiff[$key] = $oldContact[$key];
        $newDiff[$key] = $newContact[$key];
    }
}

echo "Old Diff: " . var_export($oldDiff, true) . "\n";
echo "New Diff: " . var_export($newDiff, true);

输出:

Old Diff: array (
  'name' => 'Joeahkd',
  'address' => '123 Masjdhfin',
  'city' => 'Spring',
  'a-key-only-in-old' => 'foo',
)
New Diff: array (
  'name' => 'Joe',
  'address' => '123 Main St',
  'city' => 'Springville',
  'a-key-only-in-new' => 'bar',
)

虽然它的计算时间可能较短,但我发现它的可读性并不比两次 array_diff_assoc() 调用高,而且我不确定它是否始终优于简单的本机函数调用。

如果一个项目保证有两个长度和键相同的数组,那么foreach()中的数组并集(+)可以只替换为其中之一可以完全删除依赖于 isset() 的数组和条件——如@shotdsheriff 的回答所示。

这是我推荐的@shotdsheriff 回答版本:(Demo)

$oldDiff = [];
$newDiff = [];
foreach ($oldContact as $key => $value) {
    if ($value !== $newContact[$key]) {
        $oldDiff[$key] = $value;
        $newDiff[$key] = $newContact[$key];
    }
}