Codex73
Codex73

Reputation: 5766

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
        )
)

Upvotes: 0

Views: 90

Answers (3)

Mike Brant
Mike Brant

Reputation: 71384

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.

Upvotes: 2

Don't Panic
Don't Panic

Reputation: 41810

Because service_code is unique across array A, you can use array_column to reindex array A using service_code.

$array_a = array_column($array_a, null, 'service_code');

Then flip array B so that its values become keys

$array_b = array_flip($array_b);

Then you can use array_intersect_key to get your result.

$result = array_intersect_key($array_a, $array_b);

Or all in one statement if you like:

$result = array_intersect_key(
    array_column($array_a, null, 'service_code'), array_flip($array_b));

Upvotes: 0

u_mulder
u_mulder

Reputation: 54831

Your solution is array_filter:

$filtered = array_filter(
    $array1,
    function($v) use ($array2) {
        return in_array($v['service_code'], $array2);
    }
);

Upvotes: 2

Related Questions