Skip to content
Advertisement

Most efficient way to find bidirectional differences between associative arrays

I have a contact array with a set of values which I need to update. I also have a contactChangeLog array that will save the diff between an existing and updated contact. Only the keys which get updated need to be saved to the log. So with 2 contact arrays:

$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',
);

I can use array_diff_assoc()…

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

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

However, I am iterating twice through each array and something I learned about big O notation tells me that isn’t right. Is there a more algorithmically efficient way of returning the updated keys and both existing and updated values? The result should be:

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

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

Advertisement

Answer

You can try 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 will return the keys/values from the input array if the callback function we specify returns true during each key/value iteration and those will be our $previous. While we do the checking in the callback function we construct $updated.

UPDATE:

Using array_filter with array_intersect_key even cut the code further:

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

$updated = array_intersect_key($newContact, $previous);
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement