Nupur Sharma
Nupur Sharma

Reputation: 11

Filter 2d array by multiple columns in another 2d array which doesn't contain all columns in the first array

I have below two arrays:

$a = [
    ['code' => '123', 'name' => 'ABC',],
    ['code' => '456', 'name' => 'XYZ',],
];

$b = [
    ['code' => '123', 'name' => 'ABC', 'price' => '34'],
    ['code' => '456', 'name' => 'PQR', 'price' => '56'],
    ['code' => '456', 'name' => 'XYZ', 'price' => '90'],
];

I want to create a third array where in the combination of code and name matches, like this:

$c = [
    ['code' => '123', 'name' => 'ABC', 'price' => '34'],
    ['code' => '456', 'name' => 'XYZ', 'price' => '90'],
]

I don't want the second row of $b to be retained since the combination of code and name in that row does not match with a row in array $a

Upvotes: -2

Views: 89

Answers (2)

mickmackusa
mickmackusa

Reputation: 48031

Your data structures are not built well for the required comparison. It would be easier if both of the arrays had "compound keys" instead of indexes. I mean, if the rows in the arrays looked like '123_ABC' => ['code' => '123', 'name' => 'ABC',] then making direct key-based comparisons would be a breeze.

Using what you have and with the intention of reducing the total iterations over the arrays, I recommend nested loops with early breaks in the inner loop as soon as a match is found.

Code: (Demo)

$whiteList = [
    ['code' => '123', 'name' => 'ABC',],
    ['code' => '456', 'name' => 'XYZ',],
];

$pricedList = [
    ['code' => '123', 'name' => 'ABC', 'price' => '34'],
    ['code' => '456', 'name' => 'PQR', 'price' => '56'],
    ['code' => '456', 'name' => 'XYZ', 'price' => '90'],
    ['code' => '456', 'name' => 'GHI', 'price' => '70'],
];

$result = [];
foreach ($pricedList as $pricedRow) {
    foreach ($whiteList as $whiteRow) {
        if ($whiteRow['code'] === $pricedRow['code'] && $whiteRow['name'] === $pricedRow['name']) {
            $result[] = $pricedRow;
            continue 2;  // no reason to keep iterating inner loop after match is found
        }
    }
}
var_export($result);

Or more elegantly, use array_intersect_assoc() with each row of the whitelist array. (Demo)

$result = [];
foreach ($pricedList as $pricedRow) {
    foreach ($whiteList as $whiteRow) {
        if ($whiteRow === array_intersect_assoc($whiteRow, $pricedRow)) {
            $result[] = $pricedRow;
            continue 2;  // no reason to keep iterating inner loop after match is found
        }
    }
}
var_export($result);

Output (from either snippet):

array (
  0 => 
  array (
    'code' => '123',
    'name' => 'ABC',
    'price' => '34',
  ),
  1 => 
  array (
    'code' => '456',
    'name' => 'XYZ',
    'price' => '90',
  ),
)

Upvotes: 1

mickmackusa
mickmackusa

Reputation: 48031

By unconditionally purging the potential price element before making full row comparisons inside of array_uintersect(), you can filter the array with price data by the array without prices.

Code: (Demo)

var_export(
    array_uintersect(
        $pricedList,
        $whiteList,
        function($a, $b) {
            unset($a['price'], $b['price']);
            return $a <=> $b;
        }
    )
);

Upvotes: 0

Related Questions