W poprzednim wpisie o tablicach w ActionScript, pisałam o podstawach tworzenia i manipulowania tym typem danych. W tym poście przejdziemy do bardziej interesujących i zaawansowanych zagadnień dotyczących tablic.

Iteracja po elementach tabeli

Bardzo często używając tablic, musimy wykonać jakieś operacje na każdym jej elemencie. W ActionScript 3.0 są dwa sposoby na iterację po elementach tablicy.

Pętla for

Dobrze znana pętla w większości języków programowania. Jej użycie wygląda następująco:

 var someArray = [1, 2, 3];

  for(var i:int = 0; i < someArray.length; i++)
  {
      trace(someArray[i]);
   }

metoda forEach()

Jest to nowa metoda obiektu Array (pojawiła się w wersji 3.0 ActionScript). Bardzo przydatna - powoduje wykonanie podanej funkcji na każdym elemencie tablicy. Zobaczmy jak jej używać:

    var someArray = [1, 2, 3];

    //funkcja wykonywana na każdym elemencie tablicy
    function double(element:*, index:int, arr:Array):void
    {
        if(!isNaN(element))
        {
            trace(2*element);
         }
    }

    someArray.forEach(double); //wyświetli 2 4 6

Jak widać funkcja przekazywana do metody forEach musi mieć trzy argumenty:

  1. element - parametr reprezentuje element tablicy, typ * oznacza dowolny typ
  2. index - to indeks aktualnie przetwarzanego elementu tablicy
  3. arr - tablica, na której elementach wykonujemy funkcję

Funkcja ta zwraca void.

Wykonanie operacji na wszystkich elementach tablicy

Metoda forEach nie jest jedynym dostępnym sposobem przeprowadzenia operacji na wszystkich elementach tablicy. ActionScript udostępnia jeszcze 3 ciekawe metody:

  1. every()
  2. some()
  3. filter()

Do wszystkich tych metod, jako argument, również musimy przekazać funkcję. Funkcja ta przyjmuje te same argumenty co funkcja przekazywana do metody forEach, z tą różnicą, że taka funkcja musi zwracać Boolean a nie void. Zatem ogólny zapis funkcji przekazywanej do tych 3 metod wygląda następująco:

    function someFunction(element:*, index:int, arr:Array):Boolean
    {
        ....
    }

Te trzy metody powstały po to, by umożliwić sprawdzenie wszystkich elementów tablicy pod kątem określonego warunku.

Metoda every()

Metoda ta sprawdza warunek na każdym elemencie tablicy, aż do momentu, gdy któryś z nich zwróci false. Jeżeli tak się stanie, funkcja zatrzymuje się i zwraca false. Jeżeli żaden z elementów tablicy, po sprawdzeniu na nim warunku, nie zwróci false, wówczas metoda zwraca true. Zatem metoda ta jest przydatna, jeżeli chcesz sprawdzić warunek: "Czy każdy element spełnia mój warunek?".

Zobaczmy jak to działa w praktyce:

    var arr:Array = [1,2,3,4,5];

    //funkcja sprawdzająca czy element jest mniejszy od zera
    function elementLessZero(element:*, index:int, arr:Array):Boolean
    {
        return element < 0;
    }

    //funkcja sprawdzająca czy element jest większy od zera
    function elementMoreZero (element:*, index:int, arr:Array):Boolean
    {
        return element > 0;
    }

    //sprawdzamy
    trace(arr.every(elementLessZero)); // wyświetli false
    trace(arr.every(elementMoreZero)); //wyświetli true

Metoda some()

Metoda ta, sprawdza zdefiniowany warunek, na każdym elemencie tablicy, aż do momentu, gdy któryś z nich zwróci true. Jeżeli tak się stanie, metoda zatrzymuje się i zwraca true. Jeżeli żaden z elementów tablicy, po wykonaniu na nim warunku, nie zwróci true, wówczas cała metoda zwraca false. Zatem, jak łatwo się domyślić, za pomocą tej metody sprawdzamy warunek: "Czy którykolwiek z elementów tablicy spełnia mój warunek?"

    var arr:Array = [1,2,3,4,5];

    //funkcja sprawdzająca czy element jest mniejszy od zera
    function elementLessTwo(element:*, index:int, arr:Array):Boolean
    {
        return element < 2;
    }

    //funkcja sprawdzająca czy element jest większy od zera
    function elementMoreFive (element:*, index:int, arr:Array):Boolean
    {
        return element > 5;
    }

    //sprawdzamy
    trace(arr.some(elementLessTwo)); // wyświetli true
    trace(arr.some(elementMoreFive)); //wyświetli false

Metoda filter()

Jak sama nazwa wskazuje, ta funkcja służy do filtrowania elementów tablicy. Zwraca ona nową tablicę, złożoną z tych elementów, które spełniają zdefiniowany przez nas warunek. Przykład:

    var arr:Array = [1,2,3,4,5];

    //funkcja sprawdzająca czy element jest mniejszy od zera
    function elementLessTwo(element:*, index:int, arr:Array):Boolean
    {
        return element < 2;
    }

    //filtrujemy
    trace(arr.filter(elementLessTwo)); // wyświetli 1

Metoda map()

Czasami możesz potrzebować wykonać jakąś operację na każdym elemencie tablicy, ale bez zmiany pierwotnej tablicy. Przydatna okaże się wtedy metoda map.

    var arr:Array = [1,2,3,4,5];

    //funkcja zwiększa element o 2
    function addTwo(element:*, index:int, arr:Array):Number
    {
        return element + 2;
    }

    var newArr:Array = arr.map(addTwo);
    trace(arr); //wyświetli 1,2,3,4,5
    trace(newArr); //wyświetli 3,4,5,6,7

Sprawdzanie, czy element znajduje się w tablicy

Jeżeli potrzebujesz sprawdzić, czy element istnieje w tablicy, posłuż się metodą indexOf. Metoda ta zwraca indeks szukanego elementu lub -1 jeżeli nie ma elementu w tablicy.

   var arr:Array = ["jeden", "dwa", "trzy"];

   if(arr.indexOf("jeden") != -1)
   {
        trace("indeks szukanego elementu: " + arr.indexOf("jeden")); //wyświetli 0
   }

Sortowanie tablic

Używając tablic, najczęściej nie jest dla Ciebie istotna kolejność elementów. Czasem jednak musisz posortować tablicę. Możesz wówczas skorzystać z jednej z metod sortujących, udostępnianych w ActionScript.

metoda reverse()

Metoda ta po prostu odwraca kolejność elementów w tablicy.

   var arr:Array = [1,2,3,4,5];

   trace(arr.reverse()); //wyświetli 5,4,3,2,1

metoda sort()

To znacznie ciekawsza metoda, gdyż pozwala na sortowanie elementów w oparciu o przekazany algorytm. Jeżeli nie przekażemy do niej żadnego algorytmu, funkcja posortuje elementy alfabetycznie.

Algorytm sortowania określamy za pomocą funkcji, której ogólna definicja wygląda następująco:

   function sortFunction(value1:*, value2:*):Number

Jak widać przyjmuje ona jako argumenty dwie wartości do porównania. W zależności od wyniku porównania zwracamy odpowiednią wartość numeryczną:

  • jeżeli value1 ma się znaleźć przed value2, to zwracamy -1,
  • jeżeli value1 jest równe value2, to zwracamy 0,
  • jeżeli value1 ma się znaleźć po value2, to zwracamy 1

Przypuśćmy, że chcemy posortować tablicę zawierającą obiekty Battle. Obiekty reprezentują bitwy w historii Polski. Obiekt Battle został zdefiniowany następująco:

package pl.flexair
{
	public class Battle
	{
		public var year:int;
		public var place:String;

		public function Battle(year:int, place:String)
		{
			this.year = year;
			this.place = place;
		}

		public function toString():String
		{
			return "Rok " + this.year + ": " + this.place;
		}
	}
}

Zdefiniujmy sobie również tablicę historyBattle, która będzie przechowywała obiekty Battle:

var historyBattle:Array = [ new Battle(1621, "Chocim"), new Battle(1410, "Grunwald") ,new Battle(1610, "Kłuszyn")];

Następnie zdefiniujmy funkcję, która będzie sortować naszą tablicę po dacie bitwy i posortujmy naszą tablicę:

function sortByYear(battle1:Battle, battle2:Battle):Number
			{
				if(battle1.year == battle2.year)
				{
					return 0;
				} else if (battle1.year < battle2.year)
				{
					return -1;
				} else {
					return 1;
				}
			}

			trace(historyBattle.sort(sortByYear)); //wyświetli Rok 1410: Grunwald,Rok 1610: Kłuszyn,Rok 1621: Chocim

Teraz przypuśćmy, że w innym miejscu aplikacji potrzebujemy posortować elementy w tablicy, ale tym razem w odwrotnej kolejności. Czy musimy pisać kolejną funkcję sortującą? W takim przypadku wystarczy, że posłużymy się specjalną flagą, którą możemy przekazać jako kolejny parametr metody sort(). Flagi są to stałe statyczne, zdefiniowane w klasie Array. W naszym przypadku posłużymy się flagą Array.DESCENDING:

trace(historyBattle.sort(sortByYear, Array.DESCENDING)); //wyświetli Rok 1621: Chocim,Rok 1610: Kłuszyn,Rok 1410: Grunwald

Do metody sort można przekazywać więcej niż 1 flagę. Kolejne flagi łączymy za pomocą operatora bitowego "lub" (|), np.

trace(historyBattle.sort(sortByYear, Array.DESCENDING | Array.RETURNINDEXEDARRAY));

Oto jakie mamy flagi do dyspozycji w klasie Array:

  • CASEINSENSITIVE - przy sortowaniu nie jest uwzględniana wielkość liter (domyślnie jest)
  • DESCENDING - powoduje sortowanie elementów od najwyższej wartości do najmniejszej
  • NUMERIC - jeżeli nie użyjesz tej flagi, przed sortowaniem wszystkie liczby są zamieniane na tekst. Użycie tej flagi zapobiega temu, więc sortujemy rzeczywiście liczby, a nie ich tekstowe odpowiedniki
  • RETURNINDEXEDARRAY - powoduje, że w wyniku sortowania zwracana jest nowa tablica. Tablica, na której wykonujemy sortowanie pozostaje bez zmian
  • UNIQUESORT - jej użycie powoduje zatrzymanie i zwrócenie przez funkcję sortującą 0, jeżeli znajdzie ona w tablicy dwa identyczne elementy

metoda sortOn()

Na koniec zostawiłam bardzo ciekawą metodę sortOn. Służy ona do sortowania po właściwościach obiektu. Jako argument przyjmuje ona tablicę z nazwami (nazwą) właściwości wg których chcemy posortować naszą tablicę z obiektami. Zatem powyższy przykład z tablicą historyBattle można by rozwiązać bez pisania własnego algorytmu sortowania, po prostu tak:

trace(historyBattle.sortOn(["year"]));

Sortowanie przy pomocy tej metody można również modyfikować za pomocą wcześniej wspomnianych flag.