Tablice jako stosy, kolejki lub zbiory #1

Tablice często używane są jako stosy (LIFO – Last In First Out) lub kolejki (FIFO – First In First Out). PHP bardzo upraszcza nam to podejście do tablic dostarczając zestawu funkcji wyciągania i wkładania poszczególnych elementów ze stosów / kolejek. Do tego czasem zachodzi potrzeba potraktować tablice jako zbiory danych. Tutaj też twórcy języka usprawniają nam pracę dostarczając odpowiednich metod.

Stosy

$stack = array();
 
array_push($stack, 'foo', 'bar', 'baz');
print_r($stack);
 
$last_in = array_pop($stack);
echo 'Last in:' . $last_in;
Powyższy kod wyświetli nam:

Array
(
    [0] => foo
    [1] => bar
    [2] => baz
)


Last in:baz

Bajecznie proste :) Jak widać, by operować na tablicy jak na stosie potrzebujemy dwóch funkcji: array_push() oraz array_pop().

array_push()

int array_push (array &$array, mixed $var [, mixed $...])
Funkcja ta traktuje tablicę $array jak stos i „wpycha” podane elementy na jej koniec. Tak naprawdę, daje to taki sam efekt jakbyśmy napisali:
$stack[] = 'foo';
$stack[] = 'bar';
$stack[] = 'baz';
Ale jak nie od dziś wiadomo, PHP lubi upraszczać nam kodowanie na każdym kroku :) Jest jednak drobna różnica między powyższym sposobem „upychania” elementów do tablicy (za pomocą operatora tablicowego []) a array_push(). Zakładając, że $stack nie jest tablicą, to pisząc coś takiego: $stack[] = ‘foo’;, PHP przekonwertuje $stack do typu tablicowego. array_push() natomiast wygeneruje ostrzeżenie jeśli pierwszy przekazany do niej argument nie jest tablicą. Wartość zwracana przez funkcję to nowa, aktualna ilość elementów w tablicy.
Jeśli używasz array_push() do dodania do tablicy tylko jednego elementu – powinieneś zamiast niej użyć zwykłego operatora []. Co prawda array_push() umożliwia taką operację, jednak jest to nieopłacalne z perspektywy wydajności skryptu. Po prostu to samo można osiągnąć używając operatora [], a do tego podczas dodawania elementu nie jest wywoływana żadna funkcja.

array_pop()

mixed array_pop (&$array)
Ta funkcja z kolei zwraca ostatni element z tablicy oraz usuwa go z niej. Jeśli tablica jest pusta zostanie zwrócony NULL. Natomiast jeśli funkcja zostanie wywołana dla zmiennej nietablicowej, zostanie wygenerowane ostrzeżenie i także zwrócona wartość NULL.

Kolejki

Jeśli zdecydujemy się traktować tablice jak kolejki, możemy użyć funkcji array_unshift() do dodawania elementów na początku tablicy oraz array_shift() do ich usuwania z początku tablicy.
$stack = array('qux', 'bar', 'baz');
 
$first_element = array_shift($stack);
echo 'First: ' . $first_element;
 
array_unshift($stack, 'foo', 'sally');
 
print_r($stack);
Kod ten wyświetli:

First: qux
Array
(
    [0] => foo
    [1] => sally
    [2] => bar
    [3] => baz
)

array_unshift()

int array_unshift (&$array, mixed $var [, mixed $...])
Funkcja ta dodaje element lub elementy na początku tablicy. Wszystkie klucze numeryczne są kasowane i elementy są numerowane od początku, zaczynając od zera. Klucze asocjacyjne nie są modyfikowane. Funkcja zwraca nową, aktualną ilość elementów w tablicy. Jeśli jako pierwszy argument nie przekażemy tablicy, array_unshift() wygeneruje ostrzeżenie i zwróci NULL.

array_shift()

mixed array_shift (array &$array)
Działanie tej funkcji jest analogiczne do array_pop() tylko że o ile tamta zwracała i usuwała ostatni element tablicy, o tyle ta zwraca i usuwa pierwszy element tablicy. Trzeba jeszcze dodać, że wszystkie klucze numeryczne będą skasowane i tablica będzie indeksowana od zera. Klucze asocjacyjne nie są ruszane.

array_pad()

Pisząc o operacjach przycinania tablicy to na początku, to na końcu, dodawania elementów itp. przypomniały mi się jeszcze dwie funkcje, które też niejako mogą przydać się w tego typu operacjach. Pierwszą z nich jest array_pad() czyli dopełnienie tablicy.
array array_pad (array $input, int $pad_size, mixed $pad_value)
Funkcja ta jest bardzo prosta w użyciu i nieskomplikowana w działaniu. Najprościej mówiąc, dopełnia ona tablicę $input do rozmiaru zdefiniowanego przez $pad_size wartością $pad_value. Jeśli $pad_size jest dodatnie, wówczas tablica dopełniana jest do prawej, jeśli ujemne – do lewej. Natomiast jeśli wartość bezwzględna $pad_size jest mniejsza lub równa rozmiarowi tablicy $input nie zachodzi żadne dopełnianie. Jak widać na powyższej definicji, funkcja array_pad() nie pracuje na oryginalnej tablicy, tylko na jej kopii, toteż wartością przez nią zwracaną jest dopełniona tablica. Jeśli natomiast podamy jej w pierwszym argumencie zmienną inną niż tablica, zostanie wygenerowane ostrzeżenie oraz zwrócona wartość NULL.

array_fill()

array array_fill(int $index, int $num, mixed $value)
Funkcja array_fill() jak widać nie wymaga od nas podania żadnej tablicy. Generuje ona natomiast tablicę na podstawie wytycznych, które jej podamy. Są to: indeks, od którego ma się zacząć numeracja elementów, czyli parametr $index. Następnie podajemy ilość elementów ($num) oraz wartość, jaką chcemy wypełnić tablicę. Działa to mniej więcej tak:
$b = array_fill(5, 10, 'sally');
print_r($b);
Kod wyświetli:

Array
(
    [5] => sally
    [6] => sally
    [7] => sally
    [8] => sally
    [9] => sally
    [10] => sally
    [11] => sally
    [12] => sally
    [13] => sally
    [14] => sally
)

Funkcja array_fill() wygeneruje błąd poziomu E_WARNING jeśli parametr określający ilość elementów ($num) jest mniejszy od zera.

Zbiory

Niektóre pehapowe funkcje zostały zaprojektowane specjalnie do operowania na tablicach traktując je jako zbiory danych. I tak na początek mamy funkcję array_diff(), która zwraca nam różnicę dwóch, lub więcej tablic.

array_diff()

array array_diff(array $array1, array $array2 [, array $...])
Funkcja porównuje $array1 z $array2 i zwraca różnicę również w postaci tablicy. Możemy przekazać też więcej niż jedną tablicę do porównania, wówczas dostaniemy tablicę elementów niewystępujących w żadnej z podanych tablic.
$a = array(1,2,3,4,5,6,7,8,9,10);
$b = array('foo' => 1, 'bar' => 3,5,6);
$c = array(2,8,9);
 
$wynik = array_diff($a,$b,$c);
 
print_r($wynik);
Wyświetli:

Array
(
    [3] => 4
    [6] => 7
    [9] => 10
)

Zauważmy, że funkcja ignoruje podczas porównywania klucze. Jeśli chcesz sprawdzać różnicę na zasadzie par klucz => wartość powinieneś użyć funkcji array_diff_assoc().

array_diff_assoc()

array array_diff_assoc(array $array1, array $array2 [, array $...])
Działa na podobnej zasadzie co opisywana wyżej funkcja z tą różnicą, że ta bierze pod uwagę dodatkowo zgodność kluczy.
$a = array('foo' => 'bar', 'baz' => 'bar', 'bar', 'bar');
$b = array('bar');
 
$wynik = array_diff_assoc($a,$b);
 
print_r($wynik);
Kod ten pokaże na ekranie:

Array
(
    [foo] => bar
    [baz] => bar
    [1] => bar
)

array_diff_key

Funkcja ta, podobnie jak dwie powyższe, wylicza różnicę tablic i zwraca ją w postaci oddzielnej tablicy, jednak przy porównywaniu elementów, bierze ona pod uwagę nie wartości, a klucze.
array array_diff_key(array $array1, array $array2 [, array $ ...])

array_udiff()

Jeżeli ktoś myślał, że wymienione trzy funkcje to wszystko, co PHP nam dostarcza do wyliczania różnic między tablicami, to był w dużym błędzie. Poza tymi dwoma, powiedzmy, podstawowymi funkcjami, dostajemy jeszcze szereg innych funkcji umożliwiających nam definiowanie własnych sposobów porównywania elementów. Jedną z nich jest funkcja array_udiff().
array array_udiff(array $array1, array $array2 [, array $...], callback $data_compare_func)
Kiedy jej używamy? W momencie, kiedy potrzebny jest nam bardziej złożony mechanizm porównywania tablic, niż ten, który dostarcza PHP. Jednym z takich przypadków będzie np. taki, kiedy musimy porównać ze sobą dwie tablice różniące się wymiarem. Rozważmy taki kod:
function compr($a, $b)
{
    $aVal = is_array($a) ? $a['nazwisko'] : $a;
    $bVal = is_array($b) ? $b['nazwisko'] : $b;
 
    return strcmp($aVal, $bVal);
}
 
$aktorzy = array(
    array('imie' => 'Tom',
          'nazwisko' => 'Cruise'),
    array('imie' => 'Jack',
          'nazwisko' => 'Nicholson'),
    array('imie' => 'Brad',
          'nazwisko' => 'Pitt')
);
 
$nazwiska = array('Cruise', 'Pazura', 'Pitt');
 
$result = array_udiff($aktorzy, $nazwiska, 'compr');
 
print_r($result);
Mamy dwie tablice: dwuwymiarową $aktorzy i jednowymiarową $nazwiska. Powiedzmy, że chcę z tablicy $aktorzy wybrać tylko te tablice, których element o kluczu ‘nazwisko’ nie pokrywa się z żadnym elementem tablicy $nazwiska. Wiadomo, że można pokombinować w „normalny” sposób – zagnieździć kilka pętli, dołożyć kilka if’ów i osiągniemy ten efekt. Pytanie tylko – po co? Powyższy kod robi dokładnie to, co opisałem, mianowicie wyświetli on:

Array
(
    [1] => Array
        (
            [imie] => Jack
            [nazwisko] => Nicholson
        )

)

Dlaczego? A no dlatego, że tylko Nicholsona nie ma w tablicy $nazwiska. A teraz pytanie? Co mi się wyświetli, jeśli w wywołaniu array_udiff zamienię kolejnością porównywane tablice? Myślę, że jeśli zrozumiałeś na powyższym przykładzie o co chodzi w działaniu tej funkcji, już znasz odpowiedź, jeśli nie – sam sprawdź, ja nie powiem ;)

array_udiff_assoc

array array_udiff_assoc(array $array1, array $array2 [, array $...], callback $data_compare_func)
Ta funkcja działa bardzo podobnie do powyższej, z tą różnicą, że podczas porównanie bierze pod uwagę również zgodność kluczy porównywanych elementów.
Posłużę się kodem z powyższego przykładu, tylko zamienię array_diff na array_diff_assoc.
function compr($a, $b)
{
    $aVal = is_array($a) ? $a['nazwisko'] : $a;
    $bVal = is_array($b) ? $b['nazwisko'] : $b;
 
    return strcmp($aVal, $bVal);
}
 
$aktorzy = array(
    array('imie' => 'Tom',
          'nazwisko' => 'Cruise'),
    array('imie' => 'Jack',
          'nazwisko' => 'Nicholson'),
    array('imie' => 'Brad',
          'nazwisko' => 'Pitt')
);
 
$nazwiska = array('Cruise', 'Pazura', 'Pitt');
 
$result = array_udiff_assoc($aktorzy, $nazwiska, 'compr');
Kod ten wyświetli dokładnie to samo co w przypadku array_diff. Dlaczego? W tablicy dwuwymiarowej element przypisany do Toma Cruisea ma indeks równy zero, tak samo jak element o wartości ‘Cruise’ w tablicy $nazwiska. Podobnie sprawa wygląda z Bradem Pittem. Jego indeks równy jest dwa zarówno w pierwszej jak i w drugiej tablicy. Zmodyfikujmy więc nieco tablicę $nazwiska:
function compr($a, $b)
{
    $aVal = is_array($a) ? $a['nazwisko'] : $a;
    $bVal = is_array($b) ? $b['nazwisko'] : $b;
 
    return strcmp($aVal, $bVal);
}
 
$aktorzy = array(
    array('imie' => 'Tom',
          'nazwisko' => 'Cruise'),
    array('imie' => 'Jack',
          'nazwisko' => 'Nicholson'),
    array('imie' => 'Bradd',
          'nazwisko' => 'Pitt')
);
 
$nazwiska = array('Cruise', 'Pazura','trzeci' => 'Pitt');
 
$result = array_udiff_assoc($aktorzy, $nazwiska, 'compr');
Teraz wynik będzie się różnił, mianowicie wygląda on tak:

Array
(
    [1] => Array
        (
            [imie] => Jack
            [nazwisko] => Nicholson
        )

    [2] => Array
        (
            [imie] => Bradd
            [nazwisko] => Pitt
        )

)

O ile, nazwisko ‘Pitt’ się zgadza zarówno w pierwszej jak i drugiej tablicy, o tyle klucze już są inne.

array_udiff_uassoc

array array_udiff_uassoc(array $array1, array $array2 [, array $ ...], callback $data_compare_function, callback $key_compare_function)
Funkcja ta działa na takiej samej zasadzie jak dwie poprzednie, z tą różnicą, że pozwala ona zdefiniować dwie oddzielne metody – jedną do porównywania wartości, drugą do kluczy. Myślę, że jeśli zrozumiałeś o co chodzi na dwóch poprzednich przykładach, to nie potrzeba się zagłębiać w jej działanie :)

array_diff_ukey

array array_diff_ukey(array $array1, array $array2 [, array $ ... ], callback $key_compare_func)
Po raz kolejny, bardzo podobna w działaniu funkcja co wcześniej opisane. array_diff_ukey przy porównaniu bierze pod uwagę tylko klucze, a samo porównanie wykonywane jest za pomocą funkcji dostarczonej przez użytkownika.

array_diff_uassoc

array array_diff_uassoc(array $array1, array $array2 [, array $ ... ], callback $key_compare_func)
Ostatnia funkcja z rodziny wyliczających różnicę zbiorów :) Jej działanie jest niemalże identyczne do array_diff_assoc tzn. funkcja przy porównaniu kieruje się wartościami i dodatkowo sprawdza zgodność kluczy, z tym że owo sprawdzanie zgodności kluczy odbywa się za pomocą funkcji użytkownika.

Na tym kończę ten wpis, jednak nie kończę jeszcze tematu zbiorów. Omówiłem funkcje obliczające różnicę, a co z częścią wspólną? :) Do jej wyznaczania służy array_intersect i podobnie jak w przypadku array_diff występuje kilka jej odmian, dlatego postanowiłem poświęcić jej oddzielny wpis.

, ,

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

*

Możesz użyć następujących tagów oraz atrybutów HTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">