Reputation: 563
I have an array containing rows of associative data.
$array1 = array(
array('ITEM' => 1),
array('ITEM' => 2),
array('ITEM' => 3),
);
I have a second array, also containing rows of associative data, that I would like to filter using the first array.
$array2 = array(
array('ITEM' => 2),
array('ITEM' => 3),
array('ITEM' => 1),
array('ITEM' => 4),
);
This feels like a job for array_diff()
, but how can I compare the rows exclusively on the deeper ITEM
values?
How can I filter the second array and get the following result?
array(3 => array('ITEM' => 4))
Upvotes: 47
Views: 66404
Reputation: 47990
Trust that the maintainers of PHP have optimized array_udiff()
to outperform all other techniques which could do the same.
With respect to your scenario, you are seeking a filtering array_diff()
that evaluates data within the first level's "value" (the row of data). Within the custom function, the specific column must be isolated for comparison. For a list of all native array_diff()
function variations, see this answer.
To use the first array to filter the second array (and output the retained data from the second array), you must write $array2
as the first parameter and $array1
as the second parameter.
array_diff()
and array_intersect()
functions that leverage (contain u
in their function name) expect an integer as their return value. That value is used to preliminary sort the data before actually performing the evaluations -- this is a performance optimization. There may be scenarios where if you only return 0
or 1
(not a three-way comparison), then the results may be unexpected. To ensure a stable result, always provide a comparison function that can return a negative, a positive, and a zero integer.
When comparing integer values, subtraction ($a - $b
) will give reliable return values. For greater utility when comparing float values or non-numeric data, you can use the spaceship operator when your PHP version makes it available.
Codes: (Demo)
var_export(
array_udiff($array2, $array1, fn($a, $b) => $a['ITEM'] <=> $b['ITEM'])
);
var_export(
array_udiff(
$array2,
$array1,
function($a, $b) {
return $a['ITEM'] <=> $b['ITEM'];
}
)
);
var_export(
array_udiff(
$array2,
$array1,
function($a, $b) {
return $a['ITEM'] === $b['ITEM']
? 0
: ($a['ITEM'] > $b['ITEM'] ? 1 : -1);
}
)
);
function compareArrays($a, $b) {
return $a['ITEM'] === $b['ITEM'] ? 0 : ($a['ITEM'] > $b['ITEM'] ? 1 : -1);
}
var_export(
array_udiff($array2, $array1, 'compareArrays')
);
Output for all version above:
array (
3 =>
array (
'ITEM' => 4,
),
)
When working with object arrays, the technique is the same; only the syntax to access a property is different from accessing an array element ($a['ITEM']
would be $a->ITEM
).
For scenarios where the element being isolated from one array does not exist in the other array, you will need to coalesce both $a
and $b
data to the opposite fallback column because the data from the first array and the second arrays will be represented in both arguments of the callback.
Code: (Demo)
$array1 = array(
array('ITEM' => 1),
array('ITEM' => 2),
array('ITEM' => 3),
);
$array2 = array(
array('ITEMID' => 2),
array('ITEMID' => 3),
array('ITEMID' => 1),
array('ITEMID' => 4),
);
// PHP7.4+ (arrow functions)
var_export(
array_udiff(
$array2,
$array1,
fn($a, $b) => ($a['ITEM'] ?? $a['ITEMID']) <=> ($b['ITEM'] ?? $b['ITEMID'])
)
);
Or because only the first column values from both data sets are being compared:
var_export(
array_udiff($array2, $array1, fn($a, $b) => current($a) <=> current($b))
);
Finally, because both input arrays' rows contain identical structures, you can simply compare whole rows. Demo
var_export(
array_udiff(
$array2,
$array1,
fn($a, $b) => $a <=> $b
)
);
Upvotes: 0
Reputation: 906
You can use below code to get difference.
array_diff(array_column($a1, 'ITEM'), array_column($a2, 'ITEM'));
Upvotes: 6
Reputation: 5103
Another fun approach with a json_encode
trick (can be useful if you need to "raw" compare some complex values in the first level array) :
// Compare all values by a json_encode
$diff = array_diff(array_map('json_encode', $array1), array_map('json_encode', $array2));
// Json decode the result
$diff = array_map('json_decode', $diff);
Upvotes: 20
Reputation: 1
Another solution if( json_encode($array1) == json_encode($array2) ){ ... }
Upvotes: -2
Reputation: 41
Compares array1 against one or more other arrays and returns the values in array1 that are not present in any of the other arrays.
//Enter your code here, enjoy!
$array1 = array("a" => "green", "red", "blue");
$array2 = array("b" => "green", "yellow", "red");
$result = array_diff($array1, $array2);
print_r($result);
Upvotes: -3
Reputation: 471
Having the same problem but my multidimensional array has various keys unlike your "ITEM" consistently in every array.
Solved it with: $result = array_diff_assoc($array2, $array1);
Reference: PHP: array_diff_assoc
Upvotes: -2
Reputation: 154934
A couple of solutions using array_filter
that are less performant than the array_udiff
solution for large arrays, but which are a little more straightforward and more flexible:
$array1 = [
['ITEM' => 1],
['ITEM' => 2],
['ITEM' => 3],
];
$array2 = [
['ITEM' => 2],
['ITEM' => 3],
['ITEM' => 1],
['ITEM' => 4],
];
$arrayDiff = array_filter($array2, function ($element) use ($array1) {
return !in_array($element, $array1);
});
// OR
$arrayDiff = array_filter($array2, function ($array2Element) use ($array1) {
foreach ($array1 as $array1Element) {
if ($array1Element['ITEM'] == $array2Element['ITEM']) {
return false;
}
}
return true;
});
As always with array_filter
, note that array_filter
preserves the keys of the original array, so if you want $arrayDiff
to be zero-indexed, do $arrayDiff = array_values($arrayDiff);
after the array_filter
call.
Upvotes: 8
Reputation: 20873
You can define a custom comparison function using array_udiff()
.
function udiffCompare($a, $b)
{
return $a['ITEM'] - $b['ITEM'];
}
$arrdiff = array_udiff($arr2, $arr1, 'udiffCompare');
print_r($arrdiff);
Output:
Array
(
[3] => Array
(
[ITEM] => 4
)
)
This uses and preserves the arrays' existing structure, which I assume you want.
Upvotes: 67
Reputation: 3368
I would probably iterate through the original arrays and make them 1-dimensional... something like
foreach($array1 as $aV){
$aTmp1[] = $aV['ITEM'];
}
foreach($array2 as $aV){
$aTmp2[] = $aV['ITEM'];
}
$new_array = array_diff($aTmp1,$aTmp2);
Upvotes: 20