我有两个多维数组,它们看起来都是这样的:
Array
(
[0] => Array (
'id' => 3,
'other' => 'some string',
'timestamp' => 2000-01-01 00:00:00
),
[1] => Array (
'id' => 6,
'other' => 'another string',
'timestamp' => 1835-01-01 00:00:00
)
)
我正在尝试找到一种方法来找出哪些元素出现在一个数组(
$b
)中,而不是另一个数组($a
)中,以及是否有任何元素的值发生了变化。如果 $a
是:
Array
(
[0] => Array (
'id' => 3,
'other' => 'some string',
'timestamp' => 2000-01-01 00:00:00
)
)
和
$b
是:
Array
(
[0] => Array (
'id' => 3,
'other' => 'some string',
'timestamp' => 2000-01-01 12:12:12
),
[1] => Array (
'id' => 4,
'other' => 'some string',
'timestamp' => 1900-01-01 01:12:23
)
)
那么函数应该返回:
Array
(
[0] => Array (
'id' => 3,
'other' => 'some string',
'timestamp' => 2000-01-01 12:12:12
),
[1] => Array (
'id' => 4,
'other' => 'some string',
'timestamp' => 1900-01-01 01:12:23
)
)
因为带有
id = 3
的元素已更改(timestamp
字段),并且带有 id = 4
的元素是新的,不会出现在另一个数组中。
我一直在尝试用
array_udiff
做到这一点,但我仍然不知道它是如何工作的(似乎首先对两个数组进行排序,但然后它如何进行比较?)。 array_udiff
是正确的方法还是我应该编写自定义函数?
您可以使用
array_udiff
并定义您自己的比较回调。我假设两个数组具有完全相同的结构。
您可以定义自己的回调函数,如下所示:
function comparison(Array $a, Array $b): int {
if ($a['id']==$b['id'] && $a['other']==$b['other'] && $a['timestamp']==$b['timestamp']){
return 0
}else{
return -1
}
}
如果第一个参数小于第二个参数,回调函数必须返回负整数;如果更大则为正数;如果相等则为 0。然后,您可以返回任何不同于 0 的数字来指示参数不同,如果相等则返回 0。
最后,您应该拨打
array_udiff
,如下:
array_udiff($a, $b, 'comparison')
您将获得
$a
中的元素列表,这些元素在 $b
中不是或不同。
请注意,如果要比较 2 个数组,其中一个数组的元素多于另一个数组,则应将包含新元素的数组作为第一个参数传递。
array_udiff 函数“data_compare_func”的返回是您定义的某个函数,但它必须返回小于、等于或大于零的整数,因此它可能不是满足您需求的正确函数。像这样的自定义函数应该可以满足您的需求:
// this function loops through both arrays to find a match in the other array
// it will skip entry comparisons when it goes through $arr2 because you already did it the first time
function find_diff($arr1, $arr2) {
$ret = array();
// we need to do two loops to find missing entries from both arrays
$ret = do_loop($arr1, $arr2, $ret, true);
$ret = do_loop($arr2, $arr1, $ret, false);
return $ret;
}
// this function does the looping though $arr1 to compare it to entries in $arr2
// you can skip entry comparison if $compare_entries is false
function do_loop($arr1, $arr2, $ret, $compare_entries = true) {
//look through all of $arr1 for same element in $arr2 based on $id
for ($i=0;$i<count($arr1);$i++) {
$id = $arr1[$i]['id'];
$found = false;
for ($j=0;$j<count($arr2);$j++) {
// id match found
if ($id == $arr2[$j]['id']) {
$found = true;
// only compare entries if you need to
if ($compare_entries) {
//check if other field is different
if (strcmp($arr1[$i]['other'],$arr2[$j]['other']) != 0) {
$ret = add_to_ret($arr1[$i], $ret);
break;
}
//check if timestamp field is different
if (strcmp($arr1[$i]['timestamp'],$arr2[$j]['timestamp']) != 0) {
$ret = add_to_ret($arr1[$i], $ret);
break;
}
} else {
break;
}
}
}
// entry from $arr1[$i] was not found in $arr2
if (!$found) {
$ret = add_to_ret($arr1[$i], $ret);
}
}
return $ret;
}
//this function only adds the new entry to $ret if it's ID isn't already in $ret
function add_to_ret($entry, $ret) {
$id = $entry['id'];
for ($i=0;$i<count($ret);$i++) {
if ($id == $ret[$i]['id']) {
//skip adding, its already in there
return $ret;
}
}
//add it in
$ret[] = $entry;
return $ret;
}
array_diff()
不适合过滤多维数据。 使用 array_udiff()
比较两个二维数组的行。 在这种情况下,您希望从 $b
(我将调用该数组 $input
)数组中过滤出完全在 $a
(我将调用该数组 $filterBy
)中找到的任何行。 演示
array_u*()
函数绝不能设计为执行少于 3 路的比较,因为在幕后,PHP 正在执行排序作为性能优化。换句话说,返回“1 或 0”或“-1 或 0”或“-1 或 1”可能会产生损坏/不可靠的结果。
$input = [
['id' => 3, 'other' => 'some string', 'timestamp' => '2000-01-01 12:12:12'],
['id' => 4, 'other' => 'some string', 'timestamp' => '1900-01-01 01:12:23'],
['id' => 5, 'other' => 'toker', 'timestamp' => '1900-04-20 00:04:20'],
];
$filterBy = [
['id' => 3, 'other' => 'some string', 'timestamp' => '2000-01-01 00:00:00'],
['id' => 5, 'other' => 'toker', 'timestamp' => '1900-04-20 00:04:20'],
];
var_dump(
array_udiff(
$input,
$filterBy,
fn($a, $b) => $a <=> $b
)
);
输出:
array(2) {
[0]=>
array(3) {
["id"]=>
int(3)
["other"]=>
string(11) "some string"
["timestamp"]=>
string(19) "2000-01-01 12:12:12"
}
[1]=>
array(3) {
["id"]=>
int(4)
["other"]=>
string(11) "some string"
["timestamp"]=>
string(19) "1900-01-01 01:12:23"
}
}