PHP Conference Japan 2024

array_udiff

(PHP 5, PHP 7, PHP 8)

array_udiff使用回呼函式比較資料,計算陣列的差集

說明

array_udiff(陣列 $array, 陣列 ...$arrays, 可呼叫 $value_compare_func): 陣列

使用回呼函式比較資料,計算陣列的差集。這與 array_diff() 不同,後者使用內部函式比較資料。

參數

陣列

第一個陣列。

陣列

用於比較的陣列。

value_compare_func

比較函式必須回傳一個整數。如果第一個參數分別小於、等於或大於第二個參數,則回傳值應小於零、等於零或大於零。

callback(混合型別 $a, 混合型別 $b): 整數
注意

如果比較函式回傳*非整數*值,例如 浮點數,則會將回傳值內部強制轉換為 整數。因此,諸如 0.990.1 之類的值都將被強制轉換為整數值 0,這將導致這些值被視為相等。

注意

排序回呼函式必須能夠處理來自任何陣列的任何值,且不論其原始提供的順序為何。這是因為每個個別的陣列在與其他陣列比較之前會先進行排序。例如:

<?php
$arrayA
= ["string", 1];
$arrayB = [["value" => 1]];
// $item1 和 $item2 可以是 "string"、1 或 ["value" => 1] 中的任何一個
$compareFunc = static function ($item1, $item2) {
$value1 = is_string($item1) ? strlen($item1) : (is_array($item1) ? $item1["value"] : $item1);
$value2 = is_string($item2) ? strlen($item2) : (is_array($item2) ? $item2["value"] : $item2);
return
$value1 <=> $value2;
};
?>

回傳值

回傳一個陣列,其中包含 array 中所有不存在於任何其他參數中的值。

範例

範例 #1 使用 stdClass 物件的 array_udiff() 範例

<?php
// Arrays to compare
$array1 = array(new stdClass, new stdClass,
new
stdClass, new stdClass,
);

$array2 = array(
new
stdClass, new stdClass,
);

// Set some properties for each object
$array1[0]->width = 11; $array1[0]->height = 3;
$array1[1]->width = 7; $array1[1]->height = 1;
$array1[2]->width = 2; $array1[2]->height = 9;
$array1[3]->width = 5; $array1[3]->height = 7;

$array2[0]->width = 7; $array2[0]->height = 5;
$array2[1]->width = 9; $array2[1]->height = 2;

function
compare_by_area($a, $b) {
$areaA = $a->width * $a->height;
$areaB = $b->width * $b->height;

if (
$areaA < $areaB) {
return -
1;
} elseif (
$areaA > $areaB) {
return
1;
} else {
return
0;
}
}

print_r(array_udiff($array1, $array2, 'compare_by_area'));
?>

以上範例會輸出

Array
(
    [0] => stdClass Object
        (
            [width] => 11
            [height] => 3
        )

    [1] => stdClass Object
        (
            [width] => 7
            [height] => 1
        )

)

範例 #2 使用 DateTime 物件的 array_udiff() 範例

<?php
class MyCalendar {
public
$free = array();
public
$booked = array();

public function
__construct($week = 'now') {
$start = new DateTime($week);
$start->modify('Monday this week midnight');
$end = clone $start;
$end->modify('Friday this week midnight');
$interval = new DateInterval('P1D');
foreach (new
DatePeriod($start, $interval, $end) as $freeTime) {
$this->free[] = $freeTime;
}
}

public function
bookAppointment(DateTime $date, $note) {
$this->booked[] = array('date' => $date->modify('midnight'), 'note' => $note);
}

public function
checkAvailability() {
return
array_udiff($this->free, $this->booked, array($this, 'customCompare'));
}

public function
customCompare($free, $booked) {
if (
is_array($free)) $a = $free['date'];
else
$a = $free;
if (
is_array($booked)) $b = $booked['date'];
else
$b = $booked;
if (
$a == $b) {
return
0;
} elseif (
$a > $b) {
return
1;
} else {
return -
1;
}
}
}

// Create a calendar for weekly appointments
$myCalendar = new MyCalendar;

// Book some appointments for this week
$myCalendar->bookAppointment(new DateTime('Monday this week'), "Cleaning GoogleGuy's apartment.");
$myCalendar->bookAppointment(new DateTime('Wednesday this week'), "Going on a snowboarding trip.");
$myCalendar->bookAppointment(new DateTime('Friday this week'), "Fixing buggy code.");

// Check availability of days by comparing $booked dates against $free dates
echo "I'm available on the following days this week...\n\n";
foreach (
$myCalendar->checkAvailability() as $free) {
echo
$free->format('l'), "\n";
}
echo
"\n\n";
echo
"I'm busy on the following days this week...\n\n";
foreach (
$myCalendar->booked as $booked) {
echo
$booked['date']->format('l'), ": ", $booked['note'], "\n";
}
?>

以上範例會輸出

I'm available on the following days this week...

Tuesday
Thursday


I'm busy on the following days this week...

Monday: Cleaning GoogleGuy's apartment.
Wednesday: Going on a snowboarding trip.
Friday: Fixing buggy code.

注意事項

注意請注意,此函式僅檢查 n 維陣列的一個維度。當然,您可以使用 array_udiff($array1[0], $array2[0], "data_compare_func"); 檢查更深層的維度。

另請參閱

新增筆記

使用者貢獻的筆記 9 則筆記

Colin
18 年前
我認為這裡使用類別的範例過於複雜,無法清楚示範此函式的功能。

array_udiff() 會遍歷 array_values($a) 和 array_values($b),並使用傳入的回呼函式比較每個值。

換句話說,array_udiff() 會使用提供的回呼函式將 $a[0] 與 $b[0]、$b[1]、$b[2] 和 $b[3] 進行比較。如果回呼函式在任何比較中返回零,則 $a[0] 將不會包含在 array_udiff() 返回的陣列中。然後它會將 $a[1] 與 $b[0]、$b[1]、$b[2] 和 $b[3] 進行比較。最後,將 $a[2] 與 $b[0]、$b[1]、$b[2] 和 $b[3] 進行比較。

例如,compare_ids($a[0], $b[0]) === -5,而 compare_ids($a[1], $b[1]) === 0。因此,$a[1] 不會從 array_udiff() 返回,因為它存在於 $b 中。

<?
$a = array(
array(
'id' => 10,
'name' => 'John',
'color' => 'red',
),
array(
'id' => 20,
'name' => 'Elise',
'顏色' => '藍色',
),
array(
'id' => 30,
'名稱' => 'Mark',
'color' => 'red',
),
);

$b = array(
array(
'id' => 15,
'名稱' => 'Nancy',
'顏色' => '黑色',
),
array(
'id' => 20,
'name' => 'Elise',
'顏色' => '藍色',
),
array(
'id' => 30,
'名稱' => 'Mark',
'color' => 'red',
),
array(
'id' => 40,
'name' => 'John',
'顏色' => '橘色',
),
);

function compare_ids($a, $b)
{
return ($a['id'] - $b['id']);
}
function compare_names($a, $b)
{
return strcmp($a['name'], $b['name']);
}

$ret = array_udiff($a, $b, 'compare_ids');
var_dump($ret);

$ret = array_udiff($b, $a, 'compare_ids');
var_dump($ret);

$ret = array_udiff($a, $b, 'compare_names');
var_dump($ret);
?>

會返回以下結果。

在第一個回傳值中,我們看到 $b 中沒有 id 為 10 的項目。
<?
array(1) {
[0]=>
array(3) {
["id"]=>
int(10)
["名稱"]=>
string(4) "John"
["顏色"]=>
string(3) "紅色"
}
}
?>

在第二個回傳值中,我們看到 $a 中沒有 id 為 15 或 40 的項目。
<?
array(2) {
[0]=>
array(3) {
["id"]=>
int(15)
["名稱"]=>
string(5) "Nancy"
["顏色"]=>
string(5) "黑色"
}
[3]=>
array(3) {
["id"]=>
int(40)
["名稱"]=>
string(4) "John"
["顏色"]=>
string(6) "橘色"
}
}
?>

在第三個回傳值中,我們看到 $a 中的所有名稱都存在於 $b 中(即使 $b 中名稱為 'John' 的項目不同,匿名函式也只比較名稱)。
<?
array(0) {
}
?>
napcoder
8 年前
請注意,比較函式也會在內部使用,以排序陣列並選擇下一輪要比較的元素。

如果您的比較函式沒有真正進行比較(例如,如果元素相等則返回 0,否則返回 1),您將收到預料之外的結果。
grantwparks at gmail dot com
16 年前
回覆:「複雜」

我認為重點是 array_udiff() 不僅可以用於比較同質陣列,如您的範例所示(而且絕對是最常見的需求),它也可以用於比較異質陣列。

考慮

<?php
function compr_1($a, $b) {
$aVal = is_array($a) ? $a['last_name'] : $a;
$bVal = is_array($b) ? $b['last_name'] : $b;
return
strcasecmp($aVal, $bVal);
}

$aEmployees = array(
array(
'last_name' => 'Smith',
'first_name' => 'Joe',
'phone' => '555-1000'),
array(
'last_name' => 'Doe',
'first_name' => 'John',
'phone' => '555-2000'),
array(
'last_name' => 'Flagg',
'first_name' => 'Randall',
'phone' => '666-1000')
);

$aNames = array('Doe', 'Smith', 'Johnson');

$result = array_udiff($aEmployees, $aNames, "compr_1");

print_r($result);
?>

這樣就能取得不在姓名列表中的「員工」資料。

陣列 ( [2] => 陣列 ( [last_name] => Flagg [first_name] => Randall [phone] => 666-1000 ) )

有趣的是,比較函式的兩個參數並非分別對應到 array1 和 array2。這就是為什麼函式內必須要有邏輯來處理兩個參數其中之一可能指向更複雜的員工陣列。(我可是吃過苦頭才學到這一點的。)
adam dot jorgensen dot za at gmail dot com
16 年前
文件中沒提到,但這個函式也會將 array1 與自身比較,移除任何重複的值...
b4301775 at klzlk dot com
13 年前
使用 array_udiff 進行多維度差異比對的快速範例

返回 $arr1 中不在 $arr2 中的值

<?php
$arr1
= array( array('Bob', 42), array('Phil', 37), array('Frank', 39) );

$arr2 = array( array('Phil', 37), array('Mark', 45) );

$arr3 = array_udiff($arr1, $arr2, create_function(
'$a,$b',
'return strcmp( implode("", $a), implode("", $b) ); ')
);

print_r($arr3);
?>

輸出:

陣列
(
[0] => 陣列
(
[0] => Bob
[1] => 42
)

[2] => 陣列
(
[0] => Frank
[1] => 39
)

)
1

希望這對某些人有所幫助
Jorge Morales (morales2k)
5 年前
我發現這是應用太空船運算符 (spaceship operator) 的理想情況,但在範例中並未使用。

以下是範例#1,在比較函式中使用太空船運算符。

<?php
// 要比較的陣列
$array1 = array(new stdclass, new stdclass,
new
stdclass, new stdclass,
);

$array2 = array(
new
stdclass, new stdclass,
);

// 設定每個物件的屬性
$array1[0]->width = 11; $array1[0]->height = 3;
$array1[1]->width = 7; $array1[1]->height = 1;
$array1[2]->width = 2; $array1[2]->height = 9;
$array1[3]->width = 5; $array1[3]->height = 7;

$array2[0]->width = 7; $array2[0]->height = 5;
$array2[1]->width = 9; $array2[1]->height = 2;

function
compare_by_area($a, $b) {
$areaA = $a->width * $a->height;
$areaB = $b->width * $b->height;

return
$areaA <=> $areaB;
}

print_r(array_udiff($array1, $array2, 'compare_by_area'));
?>

輸出結果為
陣列
(
[0] => stdClass 物件
(
[width] => 11
[高度] => 3
)

[1] => stdClass 物件
(
[寬度] => 7
[高度] => 1
)

)

我覺得能夠用
if ($areaA < $areaB) {
return -1;
} elseif ($areaA > $areaB) {
return 1;
} else {
return 0;
}

所有這些程式碼行以

return $areaA <=> $areaB;

取代,真是太棒了
dmhouse at gmail dot com
19 年前
取得不區分大小寫版本的 array_diff(或者 array_diff_assoc、array_intersect 或任何這類具有類似函式並接受回呼函式作為參數之一的函式)非常簡單的方法:

array_udiff($array1, $array2, 'strcasecmp');

之所以有效,是因為 strcasecmp() 會以不區分大小寫的方式比較兩個字串,而 array_diff() 使用 == 運算子比較兩個字串,會區分大小寫。
jared
15 年前
請注意,PHP 在將值傳送給回呼函式 *之前* 會先進行字串轉換。
aidan at php dot net
20 年前
此功能現在已在 PEAR 套件 PHP_Compat 中實作。

更多關於在不升級 PHP 版本的情況下使用此函式的資訊,請參考以下連結

http://pear.php.net/package/PHP_Compat
To Top