【问题标题】:Most efficient way to find bidirectional differences between associative arrays查找关联数组之间双向差异的最有效方法
【发布时间】:2022-02-21 11:53:34
【问题描述】:

我有一个联系人数组,其中包含一组需要更新的值。我还有一个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'
);

【问题讨论】:

    标签: php algorithm filtering big-o array-difference


    【解决方案1】:

    你可以试试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);
    

    如果我们指定的回调函数在每次键/值迭代期间返回 true,array_filter 将返回输入数组中的键/值,这些将是我们的$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);
    

    【讨论】:

    • 这很棒。比我写的和使用匿名函数更有效,我在 PHP 中从未见过这么多。
    • 我还将在每个实现之前和之后使用 microtime() 进行一些简单的基准测试。一直在与 C++ 程序员合作并讨论该语言使用的底层 C 库。
    • $newContact 如何神奇地进入自定义函数的作用域? $updated 如何脱离自定义函数的作用域?你在发布之前是否真的测试过你的建议?
    • @mickmackusa 这个答案就像 7 年前一样,所以事情发生了变化。
    • 哪个 php 版本允许您使用自定义函数范围访问全局变量?你在 2015 年测试过这段代码吗?如果这个答案确实有效,那么现在肯定会误导研究人员。
    【解决方案2】:

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

    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];
        }
    }
    

    【讨论】:

    • 如果你想在你的问题中添加一些东西,只需使用问题左下方的编辑按钮 (stackoverflow.com/posts/28209575/edit),只需编写如下内容:edit:...
    • 感谢您的评论。我确实曾在某一时刻编辑过这个问题。并且不可否认地在几天内写了原始问题。因此,当我最终发布它时,我已经想出了一个可能的答案。我确实认为它适合作为答案,但请理解您的观点。
    • 啊,如果你弄明白了,那么作为答案就可以了:D
    • 这个答案没有实现双向比较。这是一种方式。您的 sn-p 正在迭代 $newObj,然后根据 $oldObj 检查其值。它从不迭代 $oldObj 来检查 $newObj 数据。它依赖于两个数组具有相同长度和键的情况便利。您最初使用的两个 array_diff_assoc() 调用不会遭受相同的漏洞。您需要使用您的 sn-p 发送 foreach ($array1 + $array2 as $uniqueKey => $notNeededValue) {。您的 return 的第一个元素使用了错误的名称。
    【解决方案3】:

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

    代码:(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];
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-01-25
      • 2014-01-05
      • 1970-01-01
      • 1970-01-01
      • 2015-08-22
      • 2014-01-16
      • 2015-10-08
      • 2012-02-08
      • 1970-01-01
      相关资源
      最近更新 更多