0

I'm looking for an efficient way of comparing two arrays in PHP. I have a "before" and an "after" array, and I need to get arrays of specific changes. I did manage to get part of the code right (not sure about how effective it is), but I just can't seem to get the last comparison to work.

The first part of every element is essentially an ID, which stays the same even if the second element, essentially a Name, is changed - note Name1-Renamed for example. The ID is the same. Sometimes an element might be removed (see ID 1233, 'Name3-Deleted' is only in the 'before' array) or added (as in the case of ID 1230, 'Name4-New'). Also note that IDs, while unique, are NOT sorted in any particular order.

So, I would need to find the items that have been - Added (available 'after', but not 'before') - Removed (available 'before', but not 'after') - Changed (available in both, as there is an ID match, but the Name has changed)

And I can't for the life of me find an effective way to get the Changed elements (preferably without ifs or extraneous loops). Also, what do you think? Is array_udiff the fastest/best method for this particular task?

<?php
//'BEFORE' ARRAY
$arr1 = array( array(1231, 'Name1'), array(1232, 'Name2'), array(1233, 'Name3-Deleted') );

//'AFTER' ARRAY
$arr2 = array( array(1231, 'Name1-Renamed'), array(1232, 'Name2'), array(1230, 'Name4-New') );

//'ADDED' ARRAY
$arr3 = array_udiff($arr2, $arr1, create_function(
    '$a,$b',
    'return $a[0] - $b[0]; ')
    );

//'REMOVED' ARRAY
$arr4 = array_udiff($arr1, $arr2, create_function(
    '$a,$b',
    'return $a[0] - $b[0]; ')
    );

//'CHANGED' ARRAY. CAN'T GET THIS TO WORK PROPERLY. EXPECTED RESULT IS AN ARRAY FOR THE RENAMED ITEM.
$arr5 = array_udiff($arr2, $arr1, create_function(
    '$a,$b',
    'return (strcmp($a[1],$b[1]))*(strcmp($a[0],$b[0])); ')
    );


print("Elements Added\n");
print_r($arr3);
print("Elements Removed\n");
print_r($arr4);
print("Elements Renamed\n");
print_r($arr5);
?>

So, that is pretty much it. Does anybody know how to fix this issue? Thanks in advance for all your help!

  • 1
    Why don't you just reformat your data structure to be more like array('1231' => 'Name1', '1232' => 'Name2'), etc.? It would be much more efficient to compare two single dimensional arrays – Eihwaz Mar 24 '16 at 14:48
  • http://stackoverflow.com/questions/901815/php-compare-array – Hamza Zafeer Mar 24 '16 at 14:49
  • http://stackoverflow.com/questions/22354767/use-array-diff-assoc-or-get-difference-of-multidimensional-arrays/22355153#22355153 – AbraCadaver Mar 24 '16 at 15:59

2 Answers2

0

Fill results into $added & $changed arrays instead of outputting directly, if you need them for later.

$copyArr2 = $arr2;

foreach ($arr1 as $subArr1) {
    $hit = false;
    foreach ($copyArr2 as $key=>$subArr2) {
        if ($subArr1[0] == $subArr2[0]) {
            $hit = true;
            if ($subArr1[1] != $subArr2[1]) {
               print("Element changed:\n ".print_r($subArr2, true));
            }
            unset($copyArr2[$key]);
        }
    }
    if (!$hit)
       print("Element removed:\n ".print_r($subArr1, true));
}

print("Elements added:\n");
print_r($copyArr2);

Output:

Element changed:
 Array
(
    [0] => 1231
    [1] => Name1-Renamed
)
Element removed:
 Array
(
    [0] => 1233
    [1] => Name3-Deleted
)
Elements added:
Array
(
    [2] => Array
        (
            [0] => 1230
            [1] => Name4-New
        )

)

Update: Made small fixes.

Torge
  • 2,174
  • 1
  • 23
  • 33
  • The solution you added works, thank you. In the end, I used Eihwaz's version, after testing both solutions it appeared to be somewhat faster. Functionally however, the answer is perfect. – user2581135 Apr 01 '16 at 09:20
  • Thanks, but for the record. According to my tests. my solution is taking only 0.25 seconds for 100.000 iterations compared to 0.31 seconds by the other one. Which makes sense if you see how many times it needs to iterate the arrays. I did replace the prints by arrays obviously for the test. – Torge Apr 01 '16 at 10:05
0
//'BEFORE' ARRAY
$arr1 = array( array(1231, 'Name1'), array(1232, 'Name2'), array(1233, 'Name3-Deleted') );

//'AFTER' ARRAY
$arr2 = array( array(1231, 'Name1-Renamed'), array(1232, 'Name2'), array(1230, 'Name4-New') );

// Kudos to AbraCadaver for the following:
$arr1 = array_column($arr1, 1, 0);
$arr2 = array_column($arr2, 1, 0);

$added = array_diff_key($arr2, $arr1);
$deleted = array_diff_key($arr1, $arr2);
$modified = array_diff_key(array_diff($arr2, $arr1), $deleted, $added);
Eihwaz
  • 1,234
  • 10
  • 14