2013-06-06 17 views
6

Używam array_diff() do pobierania wartości z array1, które znajdują się w array2. Problem polega na tym, że usuwa wszystkie wystąpienia z tablicy 1, o czym wspomina dokumentacja PHP. Chcę, żeby to było tylko jedno po drugim.Zachowaj duplikaty podczas korzystania z array_diff

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

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

Powinno zwrócić tablicę z literą "a" i "b", zamiast tablicy z literą "b";

+0

array_unique() zwraca tablicę bez zduplikowanych wartości. Próbuję odjąć dwie tablice. array_diff() robi to, ale nie jest dokładne. – STEELHE4RT

Odpowiedz

9

tylko dla zabawy, coś, co tylko przyszło mi do głowy. Będzie działać tak długo, jak tablice zawierają ciągi:

$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]--; 
}); 

Ma tę zaletę, że idzie tylko na każdej z tablic tylko raz.

See it in action.

Jak to działa:

Najpierw częstotliwości każdego elementu w drugiej tablicy są liczone. To daje nam tablice, w których klucze są elementami, które powinny zostać usunięte z $a, a wartości to liczba przypadków, w których każdy element powinien zostać usunięty.

Następnie jeden array_filter służy do badania elementów $a i usuwania tych, które powinny zostać usunięte. Funkcja filtru używa empty do zwracania true, jeśli nie ma klucza równego badanemu elementowi lub jeśli pozostała liczba usunięć dla tego przedmiotu osiągnęła zero; Zachowanie się pasuje idealnie do rachunku.

Jeśli żadne z powyższych nie zostanie spełnione, chcemy zwrócić false i zmniejszyć liczbę usunięć o jeden. Używanie false || !$counts[$o]-- jest sztuczką, aby być zwięzłym: zmniejsza ona liczbę i zawsze ocenia się na false, ponieważ wiemy, że liczba była na początku większa niż zero (gdyby nie było, || spowodowałoby zwarcie po ocenie empty).

+0

Dwa razy. Raz dla 'array_count_values', raz dla' array_filter'. Nadal jest wydajniejszy niż powyżej, czyli O (N^2). –

+0

@ SébastienRenauld: Raz każdy plus oczywiście robi kilka zapytań hashowych podczas iterowania '$ a'. Ale mimo to nadal jest O (N + M). – Jon

+0

Nie była to krytyka, tylko drobna korekta :-) –

5

napisać funkcję, która usuwa elementy z pierwszej tablicy, jeden po drugim, coś jak:

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

Dzięki, tego właśnie szukałem. – STEELHE4RT

+0

Ten kod ma błąd. $ pos będzie, po pierwszym nie dopasowaniu pozycji w $ tablica2 w $ tablica1, (która nie jest uwzględniona w przykładzie), następnie zwróci wartość false, która w wywołaniu unset() zostanie odłożona na liczbę całkowitą zero, co skutkuje rozbrojeniem ($ tablica1 [0]). Innymi słowy, ma to bardzo poważną wadę, że jeśli istnieje element $ tablica2, którego nie ma w $ tablica1, to pierwszy element $ tablica1 nie zostanie uwzględniony dla wyniku różnicy macierzy, co spowodowało bardzo poważny błąd w moim kodzie produkcyjnym, ponieważ ślepo go skopiowałem i wkleiłem, będąc dość nieoczywistym. –

+1

tak, rzeczywiście, zredagowałem mój post, aby uwzględnić twoją poprawkę :) – Guillaume

Powiązane problemy