phparraysmultidimensional-arrayfilteringarray-difference

Remove rows from a 2d array where a specific column value is found in a flat blacklist array


Need to eliminate from Array A arrays that key 'service_code' value doesn't exist on Array B. In the example, only 'SAF' exists on Array B. Have tried a variety of array_intersect functions with no luck. Instead of doing a loop, I believe there must be a method unknown to me to accomplish this. I can also invert Array B by removing array_keys if necessary.

Array A

Array
(
    [1] => Array
        (
            [id] => 2
            [service_name] => Carpet Cleaning
            [type] => 
            [category_name] => Household
            [service_code] => SAF
            [category_code] => AA
        )

    [2] => Array
        (
            [id] => 3
            [service_name] => Floor Cleaning
            [type] => 
            [category_name] => Household
            [service_code] => MHSAF
            [category_code] => AA
        )

    [3] => Array
        (
            [id] => 4
            [service_name] => Lawn Service
            [type] => 
            [category_name] => Landscape
            [service_code] => GHF
            [category_code] => AA
        )
)

Array B

Array
(
    [0] => SAF
    [1] => SA
    [2] => MM
    [3] => METH
    [4] => OTPA
    [5] => OTP
    [6] => CBT
    [7] => SACA
    [8] => TRC
    [9] => REBT
)

Expected Result

Array
(
    [1] => Array
        (
            [id] => 2
            [service_name] => Carpet Cleaning
            [type] => 
            [category_name] => Household
            [service_code] => SAF
            [category_code] => AA
        )
)

Solution

  • At the end of the day you will doing loops no matter what with those data structures, even if the "loop" is hidden within a function call like array_filter().

    My number one suggestion would be to change Array B if possible so that you don't need to iterate it to see if a value in the array exists. A data structure like:

    [
      'SAF' => 1,
      'SA' => 1,
       ...
    ]
    

    You can achieve a data structure like this easily be executing array_flip() on the array.

    A structure where the keys contain the values you are looking for will let you do an O(1) lookup - instead of O(n) - for checking service codes from array A.

    Your code could then look like:

    $result = array_filter($array_a, function($item, $k) use ($array_b) {
        return array_key_exists($item['service_code'], $array_b);   
    });
    

    If you cannot change array b as described you would need to iterate Array B (which is what happens when you call in_array() function) in the array_filter operation:

    $result = array_filter($array_a, function($item, $k) use ($array_b) {
        return in_array($item['service_code'], $array_b);  
    });
    

    The first solution would have a run time complexity of O(n) where n is number of elements in Array A.

    The second solution would have a run time complexity of O(n*m) where m is number of elements of Array B (n is still number of elements in array A).

    Since the second solution performs so poorly, you can optimize by using array_flip()

    $service_code_keys = array_flip($array_b);
    $result = array_filter(
        $array_a,
        function($item, $k) use ($service_code_keys) {
            return array_key_exists($item['service_code'], $service_code_keys); 
        }  
    );
    

    The would improve operational complexity to O(m + n) as you take a one time O(m) hit to iterate and flip Array B. Still this is much improved over the in_array() solution.