-2

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

mickmackusa
  • 43,625
  • 12
  • 83
  • 136

2 Answers2

1

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 ($whiteList as $whiteRow) {
    foreach ($pricedList as $pricedRow) {
        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);

Output:

array (
  0 => 
  array (
    'code' => '123',
    'name' => 'ABC',
    'price' => '34',
  ),
  1 => 
  array (
    'code' => '456',
    'name' => 'XYZ',
    'price' => '90',
  ),
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • I think it would be better if you inverted the order of your `foreach` loops. – Dharman Jun 18 '20 at 16:25
  • Is this one of those Monty Hall things that I refuse to understand/comply with? I think you will have to explain the benefit to me. My intention is to only iterate the whitelist and look for matches inside of the pricelist. If I iterate the pricelist as the first loop, then I'll potentially be iterating inner loop arrays that I have no interest in (even after I have already fully satisfied all of the rows in the whitelist). You will need to compel me with a valuable justification. – mickmackusa Jun 18 '20 at 22:12
  • Usually, a whitelist is something smaller than your dataset. If your dataset is huge then you are looping more times than necessary. Also when looping this way you are effectively only selecting a matching item once. What if the mapping is 1:N? I know this question didn't have these details, but I believe the consensus is that you loop on your dataset and for each element you check if it exists in the whitelist. It makes more sense to me at least. Compare the results in this example: https://3v4l.org/uger8 – Dharman Jun 18 '20 at 22:17
  • Okay then, I guess I don't understand. My snippet wants to iterate the needles over the haystacks. It just _feels_ wrong to me to iterate the haystacks over the needles. Maybe I'll not smart enough to grasp what you are advising. – mickmackusa Jun 18 '20 at 22:21
0

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;
        }
    )
);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136