2013-06-06 21 views
6

Tôi đang sử dụng array_diff() để lấy giá trị ra khỏi mảng1 được tìm thấy trong mảng2. Vấn đề là nó loại bỏ tất cả các lần xuất hiện từ mảng 1, vì các tài liệu PHP ghi lại. Tôi muốn nó chỉ đưa ra một tại một thời điểm.Giữ bản sao trong khi sử dụng array_diff

$array1 = array(); 
$array1[] = 'a'; 
$array1[] = 'b'; 
$array1[] = 'a'; 

$array2 = array(); 
$array2[] = 'a'; 

Nó sẽ trả về mảng với một 'a' và một 'b', thay vì mảng chỉ 'b';

+0

array_unique() trả về mảng không có giá trị trùng lặp. Những gì tôi đang cố gắng làm là trừ hai mảng. array_diff() thực hiện điều này nhưng nó không chính xác. – STEELHE4RT

Trả lời

9

Chỉ cần cho những niềm vui của nó, cái gì mà chỉ lóe lên trong óc. Sẽ hoạt động miễn là các mảng của bạn chứa các chuỗi:

$a = array('a','b','a','c'); 
$b = array('a'); 

$counts = array_count_values($b); 
$a = array_filter($a, function($o) use (&$counts) { 
    return empty($counts[$o]) || !$counts[$o]--; 
}); 

Nó có lợi thế là nó chỉ đi qua từng mảng của bạn một lần.

See it in action.

Cách hoạt động:

Đầu tiên các tần số của mỗi phần tử trong mảng thứ hai được tính. Điều này cho chúng ta một mảng trong đó các khóa là các phần tử cần được loại bỏ khỏi $a và các giá trị là số lần mà mỗi phần tử cần được loại bỏ.

Sau đó, array_filter được sử dụng để kiểm tra các thành phần của $a từng cái một và xóa những phần tử cần xóa. Chức năng lọc sử dụng empty để trả lại true nếu không có khóa nào bằng với mục đang được kiểm tra hoặc nếu số lần xóa còn lại cho mục đó đã đạt đến 0; Hành vi của empty phù hợp với hóa đơn một cách hoàn hảo.

Nếu cả hai trường hợp trên không giữ được thì chúng tôi muốn trả lại false và giảm số lần xóa theo một. Sử dụng false || !$counts[$o]-- là một thủ thuật để có thể hiểu được: nó giảm số lượng và luôn đánh giá là false vì chúng ta biết rằng số đếm lớn hơn 0 để bắt đầu (nếu không, || sẽ đoản mạch sau khi đánh giá empty).

+0

Hai lần. Một lần cho 'array_count_values', một lần cho' array_filter'. Vẫn hiệu quả hơn so với ở trên, đó là O (N^2), mặc dù. –

+0

@ SébastienRenauld: Mỗi một lần, cộng với tất nhiên nó thực hiện một số tra cứu băm khi lặp lại '$ a'. Nhưng dù vậy nó vẫn là O (N + M). – Jon

+0

Không phải chỉ trích, chỉ cần chỉnh sửa nhẹ :-) –

5

Viết một số chức năng mà loại bỏ các yếu tố từ mảng đầu tiên từng người một, một cái gì đó như:

function array_diff_once($array1, $array2) { 
    foreach($array2 as $a) { 
     $pos = array_search($a, $array1); 
     if($pos !== false) { 
      unset($array1[$pos]); 
     } 
    } 

    return $array1; 
} 

$a = array('a', 'b', 'a', 'c', 'a', 'b'); 
$b = array('a', 'b', 'c'); 

print_r(array_diff_once($a, $b)); 
+0

Cảm ơn, đây là những gì tôi đang tìm kiếm. – STEELHE4RT

+0

Mã này có lỗi trong đó. $ pos sẽ, khi đầu tiên không khớp với một mục trong $ array2 trong $ array1, (trường hợp này không được bao gồm trong ví dụ), sau đó sẽ trả về false, trong đó, trong unset() gọi, sẽ được đúc thành số nguyên số không, dẫn đến unset ($ array1 [0]). Nói cách khác, điều này có một lỗ hổng rất nghiêm trọng, nếu có một phần tử trong $ array2 không có trong mảng $ 1, thì phần tử đầu tiên của mảng $ sẽ không được xem xét cho kết quả khác biệt mảng, dẫn đến một lỗi rất nghiêm trọng trong mã sản xuất của tôi bởi vì tôi đã sao chép và dán một cách mù quáng, điều này khá rõ ràng. –

+1

vâng, thực sự, tôi đã chỉnh sửa bài đăng của tôi để bao gồm sửa lỗi của bạn :) – Guillaume

Các vấn đề liên quan