metaxy quod erat demonstrandum…

4lis/090

Mootools odc. 4: Array

Odcinek czwarty kursu mootools tym razem dotyczący ważnej rzeczy jaką są tablice.

Wprowadzenie

W całym odcinku stosuje dla naszej tablicy, na której przeprowadzamy rozmaite operacje oznaczenie (tak jak z resztą w dokumentacji) myArray, żeby nikomu nie myliło się z klasą Array.
Kilka z metod przyjmuje jako jeden z parametrów funkcję. Jeżeli wyraźnie nie zaznaczono inaczej w stosujemy taką, która będzie otrzymywać 3 parametry (żaden nie jest obowiązkowy, ale żeby miało to sens przynajmniej pierwszy jest potrzebny):
fn(item, index, array) {
// tu w zależności od metody różna treść
}
Gdzie:

  1. item - kolejny element tablicy,
  2. index - indeks, pod którym znajduje się w tablicy nasz element,
  3. array - tablica, którą aktualnie przechodzimy poprzez each

W tych metodach, gdzie występuje parametr bind służy on uściślenia na co wskazywać ma this w funkcji.
Oto co oferuje nam klasa Array:

Jak widać klasa oferuje nam sporo metod. Część z nich jednak zostało wprowadzonych ponieważ niektóre przeglądarki (dobrze myślicie - IE przoduje w tej kategorii) nie oferują ich natywnie. Jeżeli dana przeglądarka wspiera takie rozwiązanie wykorzystywane jest oryginalne bo oczywiście jest szybsze. W przeciwnym wypadku emulowane są przez mootools. Często są to tylko aliasy (inne nazwy) do natywnych metod.

void each(fn[, bind])

Jeżeli komuś na pierwszy rzut oka kojarzy się pętla foreach znana z PHP to skojarzenie to jest idealne. Zasada działania ta sama - oczywiście nieco inna składnia. Jest to pętla, która z tablicy pobiera pierwszy element i go nam zwraca, następnie drugi i kolejny, kolejny aż dotrze do końca tablicy, kiedy to przerywa swoje działanie.
var myArray = [32, 21, 66, 5, 9]; // zapis równoważny z new Array(32, 21, 66, 5, 9)
myArray.each(function(item, index) {
alert('Na pozycji ' + index + ' znajduje się: ' + item);
});
W poprzednim przykładzie użyliśmy tylko 2 pierwszych argumentów funkcji ze wstępu. Teraz coś z wykorzystaniem trzeciego:
myArray.each(function(item, index, arr) { // wskaźnik tablicy został zresetowany - znów zaczynamy od zerowego elementu tablicy
var compare;
if ($chk(arr[index + 1])) {
if (item > arr[index + 1]) compare = '>';
else if (item < arr[index + 1]) compare = '<';
else compare = '=';
alert(item + ' ' + compare + ' ' + arr[index + 1]);
}
});
Oczywiście ten przypadek (jak każdy inny z użyciem tablic) równie dobrze można zrobić na pętli iteracyjnej for (var i = 0; i < myArray.length; i++). Wadą tego rozwiązania jest, że nie możemy użyć w each klasycznego break do przerwania jej działania. continue możemy osiągnąć wstawiając w jego miejsce return co powoduje zwrócenie "niczego" z funkcji i przerwanie pojedynczego obrotu.

boolean every(fn[, bind])

Metoda nieco podobna do each ponieważ jako parametr podajemy identyczną funkcję oraz też służy ona do przeglądania wszystkich elementów tablicy. W czym tkwi więc różnica? Otóż przy okazji testuje ona czy wszystkie elementy spełniają dany warunek. Np. możemy sprawdzić, czy wszystkie elementy tablicy są nieparzyste:
var myArray = [31, 23, 21, 87, 861];
var test = myArray.every(function(item) {
return item % 2; // np. 31 % 2 = 1 bo 2 * 15 + 1 reszty.
}); // test = true
var myArray = [32, 23, 21, 87, 861]; // zmieniliśmy tylko pierwszą liczbę na parzystą
var test = myArray.every(function(item) {
return item % 2; // np. 32 % 2 = 0 - zero jest równoważne w tym wypadku false
}); // test = false
Jeżeli test każdego elementu zwrócił (return w funkcji) true oznacza to, że wszystkie elementy spełniają dany warunek. Inny przykład:
var myArray = [111, 9, 99, 55, 99, 23, 44];
var test = myArray.every(function(item, index) { // testujemy czy co 3 element jest większy od 22
if (index % 3 == 0) return item > 22; // 111, 55 oraz 44 są większe od 22
return true;
}); // test = true

array filter(fn[, bind])

Kolejna funkcja bardzo podobna do poprzedniej. Tym razem jednak kopiujemy do nowej tablicy tylko te elementy, które spełniają podany warunek. W związku z tym przykład podobny do tego z metody every:
var myArray = [32, 23, 21, 82, 861]; // pierwsza i 4 liczba parzysta - reszta nie
var test = myArray.every(function(item) {
return item % 2; // zwracamy tylko liczby nieparzyste
}); // test = [23, 21, 861]

array clean()

Tym razem metoda mimo, że podobna w działaniu to nie wymagająca od nas pisania własnych recept. Służy ona do czyszczenia tablicy z niezdefiniowanych elementów (null i undefined) - trzeba pamiętać, że np. błąd dzielenia przez zero bądź nieskończoności są nieusuwane.
var myArray = [2, "pies", 0, false, true, null, function(item) { return item % 2; }, undefined, 2 / 0, true];
var test = myArray.clean(); // [2, "pies", 0, false, true, function(), Infinity, true]

number indexOf(item[, from])

Przeszukuje tablicę i zwraca indeks szukanego elementu, bądź -1 jeżeli nie udało się takiego znaleźć. Drugi parametr jest opcjonalny i służy do wskazania, od którego elementu ma zacząć szukać.
var myArray = [31, 23, 21, 87, 861, 21];
myArray.indexOf(21); // 2
myArray.indexOf(21, 3) // 5
myArray.indexOf(1222) // -1 - nie znaleziono takiego elementu

array map(fn[, bind])

Mapowanie tablicy, czyli modyfikowanie jej za pomocą jakiejś funkcji. Analogicznie jak array_map w PHP. Np. chcemy, wszystkie elementy w tablicy pomnożyć przez 2:
var myArray = [31, 23, 21, 87, 861, 21];
var newArray = myArray.map(function(item) {
return item * 2;
}); // newArray = [62, 46, 42, 174, 1722, 42]
A może w tablicy trzymamy imiona i chcemy do panów dopisać "Pan", a do pań "Pani"? W języku polskim imiona wszystkich kobiet kończą się na literę "a":
var myArray = ['Iza', 'Jan', 'Michał', 'Agnieszka', 'Magda', 'Teresa'];
var newArray = myArray.map(function(item) {
return (item[item.length - 1] == 'a' ? 'Pani' : 'Pan') + ' ' + item; // można też użyć item.charAt(item.length - 1) zamiast: item[item.length - 1]
}); // newArray = ["Pani Iza", "Pan Jan", "Pan Michał", "Pani Agnieszka", "Pani Magda", "Pani Teresa"]

boolean some(fn[, bind])

Wypisz wymaluj metoda every z tylko jedną różnicą. W every jak sama nazwa wskazuje każdy element miał spełniać podany warunek. W tym wypadku wystarczy, żeby choć jeden taki warunek spełniał:
var adamNotes = [4, 3, 4, 5, 4.5, 3.5, 2, 3]; // załóżmy, że to oceny studenta
var test = adamNotes.some(function(item) {
return item < 3; // sprawdzamy, czy którakolwiek z ocen jest mniejsza niż 3.
}); // test = true - student nie ma zaliczenia z semestru bo ma przynajmniej jedną dwóję
var magdaNotes = [4, 4.5, 4, 5, 4.5, 4.5];
var test = magdaNotes.some(function(item) {
return item <= 4.5; // sprawdzamy, czy którakolwiek z ocen jest mniejsza niż 4.5.
}); // test = false - prymus z patentem na stypendium

object associate(obj)

Tworzy obiekt z 2 tablic. Tablica, którą przekazujemy jako parametr metody służy jako zbiór kluczy. Tablica, na której operujemy jako zbiór wartości.
var names = ['Iza', 'Jan', 'Michał', 'Agnieszka', 'Magda', 'Teresa'];
var surnames = ['Nowak', 'Kowalski', 'Mieszko', 'Koc', 'Baran', 'Trela'];
var data = names.associate(surnames);
// data = { 'Nowak': 'Iza', 'Kowalski': 'Jan', 'Mieszko': 'Michał',
// 'Koc': 'Agnieszka', 'Baran': 'Magda', 'Trela': Teresa }
Jeżeli byłoby więcej wartości niż kluczy (lub odwrotnie) zostanie wykorzystane ich tyle jest w mniejszym zbiorze (nie są tworzone klucze o wartościach np. null. Jeżeli w tablicy z kluczami znajdują się powtarzające się wartości (np. u nas 2 te same nazwiska) zostanie wykorzystane ostatnie o ile będzie wystarczająca ilość wartości.

object link(array, object)

Chyba najciekawsza metoda z całej klasy, aczkolwiek obstawiam, że zbyt często nie będziecie z niej korzystać ponieważ wymaga pewnej dozy przewidywania przy projektowaniu aplikacji. Cóż więc ona robi? Tworzy obiekt, który dopasowuje sobie pierwszy element, który spełnia nasze wymagania:
var myArray = [43, 2, 8, 33, 231, 11];
var myObject = myArray.link({
small: function(item) {
return item < 10
},
medium: function(item) {
return item < 40
},
big: function(item) {
return item >= 40
}
}); // {small: 2, medium: 8, big: 43}
Jak to działa? Szuka od początku pasującego elementu, potem usuwa go z tablicy i do kolejnego pola obiektu próbuje z pozostałych dobrać jakiś pasujący. W naszym przykładzie krok po kroku:

  1. szukamy elementu w tablicy dla small, 43 nie jest mniejsze od 10, więc kolejnym elementem jest 2. 2 jest mniejsze od 10, więc zapisujemy, że od teraz small będzie 2. Usuwamy 2 z tablicy - ma teraz ona postać: [43, 8, 33, 231, 11]
  2. szukamy czegoś dla medium. 43 nie jest mniejsze od 40, ale za to kolejny element 8 już tak, więc zapisujemy do medium i z tablicy usuwamy 8. Zostaje nam: [43, 33, 231, 11]
  3. kolejnym i ostatnim szukanym elementem jest coś dla big. Na pierwszym miejscu w pozostałej nam tablicy jest 43, które jest większe od 40, więc dla nas super - pasuje. Usuwamy 43, zostaje
    [8, 33, 231, 11].
  4. Nie ma już żadnego elementu, który chcielibyśmy dopasować - koniec.

Nie musimy jednak podawać wcale funkcji jeżeli wystarczy nam prostsze porównanie typu (np. String, Number, etc.). Możemy też mieszać dla każdego elementu obiektu wybierać inne typy porównań:
var myArray = [43, 'Jan', false, 33, 231, true, 'Długi napis'];
var myObject = myArray.link({
bigNumber: function(item) {
return item > 200
},
biggerNumber: function(item) {
return item > 300
},
string: String.type,
firstBoolean: Boolean.type,
secondString: String.type
}); // { bigNumber: 231, string: 'Jan', firstBoolean: false, secondString: 'Długi napis' }
Brak w otrzymanym obiekcie pola "biggerNumber" ponieważ niczego pasującego (> 300) nie znaleziono.

boolean contains(item[, from])

Sprawdzamy, czy dana tablica zawiera poszukiwany przez nas element. Można powiedzieć, że jest to wariacja metody indexOf z tym, że zwraca tylko true - jeżeli znaleziono lub false - jeżeli nie.
var myArray = ['Adam', 'Ewa', 'Magda'];
myArray.contains('Adam'); // true
myArray.contains('Jan'); // false

array extend(array)

Połączenie ze sobą 2 tablic. Metoda nie kasuje elementów powtarzających się, więc powtórki mogą wystąpić dowolnie wiele razy.
var first = ['Iza', 'Jan', 'Michał', 'Agnieszka'];
var second = ['Tomasz', 'Marian', 'Jan', 'Iza'];
first.extend(second); // ["Iza", "Jan", "Michał", "Agnieszka", "Tomasz", "Marian", "Jan", "Iza"]
Elementy tablicy podanej jako parametr (u nas second) zostają po prostu dopisane na końcu pierwszej tablicy.

mixed getLast()

Zwraca ostatni element tablicy:
var myArray = ['Iza', 'Jan', 'Michał', 'Agnieszka'];
myArray.getLast(); // 'Agnieszka'
myArray[myArray.length - 1]; // zapis równoważny do powyższego

mixed getRandom()

Zwraca losowy element tablicy:
var myArray = ['Iza', 'Jan', 'Michał', 'Agnieszka'];
myArray.getRandom(); // nie napiszę co zwróci bo zwróci losowy element

array include(item)

Dodaje jeden element na koniec tablicy. Różnica z extend to fakt, że można dodać tylko jeden element oraz, że nie zostaną dodane powtarzające się elementy. Tablica z unikalnymi wartościami po użyciu include nadal taką pozostanie. Sprawdzając, czy dany element istnieje testowana jest zarówno zgodność typu jak i np. wielkości liter.
var myArray = ['Iza', 'Jan', 'Michał', 'Agnieszka'];
myArray.include('Jan'); // ['Iza', 'Jan', 'Michał', 'Agnieszka'] - nic nie zostało dodane bo Jan już jest
myArray.include('Tomasz'); // ['Iza', 'Jan', 'Michał', 'Agnieszka', 'Tomasz'] - dodano Tomasza

array combine(array)

Metoda identyczna z include z tym, że pozwala na dodanie tablicy do tablicy. Czyli zamiast jednego elementu mamy możliwość na raz dodać ich wiele.
var myArray = ['Iza', 'Jan', 'Michał', 'Agnieszka'];
myArray.include(['Jan', 'Magda']); // ['Iza', 'Jan', 'Michał', 'Agnieszka', 'Magda'] - dodano tylko Magdę
myArray.include(['Tomasz', 'Bogdan;]); // ['Iza', 'Jan', 'Michał', 'Agnieszka', 'Tomasz', 'Bogdan']

array erase(item)

Usuwa wszystkie wystąpienia danego elementu w tablicy.
var myArray = ['Iza', 'Jan', 'Michał', 'Agnieszka'];
myArray.erase('Michał'); // ['Iza', 'Jan', 'Agnieszka']
var myArray = ['Iza', 'Jan', 'Michał', 'Agnieszka', 'Jan', 'Magda', 'Jan'];
myArray.erase('Jan'); // ['Iza', 'Michał', 'Agnieszka', 'Magda']

array empty()

Czyści tablicę - co by się w niej wcześniej nie znajdowało zostaje wyczyszczona.
var myArray = ['Iza', 'Jan', 'Michał', 'Agnieszka'];
myArray.empty(); // []
myArray = new Array(); // to samo
myArray = []; // to również

array flatten()

Spłaszcza (sprowadza to jednego wymiaru) wielowymiarową tablicę.
var myArray = [
31, [23, 21], 87, [861, 21, 31]
]; // [31, 23, 21, 87, 861, 21, 31]
Do czego może się to przydać? Często łatwiej jest operować na tablicy jednowymiarowej szczególnie, że większość metod tylko na takich potrafi pracować.
var myArray = [
31, [23, 21], 87, [861, 21, 31]
];
myArray.erase(31); // [[23, 21], 87, [861, 21, 31]] - usunięto tylko pierwszą 31
var myArray = [
31, [23, 21], 87, [861, 21, 31]
];
myArray.flatten().erase(31); // [23, 21, 87, 861, 21]

mixed hexToRgb([array])

Konwertuje tablicę, zawierającą dane o kolorze, w formacie heksadecymalnym (szesnastkowym) ['FF', 'FF', 'FF'] na kolor RGB. Jeżeli opcjonalny parametr przyjmie wartość true zostaje zwrócona tablica. Jeżeli nie to zapis koloru używany w CSS:
var green = ['00', 'FF', '00'];
green.hexToRgb(); // rgb(0,255,0)
green.hexToRgb(true); // [0, 255, 0]

mixed rgbToHex([array])

Działanie odwrotne do hexToRgb. Z koloru w formacie RGB dostajemy zapis heksadecymalny. Możliwe tablice do konwersji to:

  • [255, 255, 255] - RGB
  • [255, 255, 255, 1] - RGBA - przeźroczystość z kanałem alpha

Metoda zwróci string z kolorem, tablicę lub słowo "transparent" jeżeli alpha (ostatni element tablicy) będzie równa 0.
var green = [0, 255, 0];
green.rgbToHex(); // #00ff00
green.rgbToHex(true); // ['00', 'ff', '00']
var red = [255, 0, 0, 0.5];
green.rgbToHex(); // #ff0000
var blue = [0, 0, 255, 1]; // transparent

array $A(iterable)

Kopiuje tablicę. Pytanie po co skoro można zastosować zapis:
var myArray = [213, 123, 23, 22];
var mySecondArray = myArray;
Owszem zapis ten jest zupełnie poprawny. Jeżeli jednak w tablicy znajdowałyby się np. obiekty DOM - choćby wszystkie linki w menu to utworzona w ten sposób kopia w zasadzie kopią by nie była. Obiekty przekazywane są poprzez referencję, więc w takiej tablicy znajdowałyby się tylko wskazania na obiekty. Mały przykład korzystając z poprzedniego:
var myArray = [213, 123, 23, 22];
var mySecondArray = myArray;
mySecondArray[0] = 0;
// myArray = [213, 123, 23, 22], mySecondArray = [0, 123, 23, 22];
Teraz to samo z tablicą zawierającą obiekty:
var myArray = [
{'first' : 0, 'second': 1},
{'test': 2},
{'name' : 'theme'}
];
var mySecondArray = myArray;
mySecondArray[0].first = 111;
// myArray[0].first = 111, mySecondArray[0].first = 111;
Co się stało? Chcieliśmy zmienić wartość w drugiej tablicy, a zmieniła się w obu. Dla tych z Was, którzy póki co nie rozumieją pojęcia referencji - druga zmienna stała się jakby tylko aliasem (inną nazwą) dla tej pierwszej. To tak samo jak powiecie: "Pies do budy" i "Pimpek do budy" - nazwy różne, ale wskazują na tą samą istotę. Należy o tym bezwzględnie pamiętać - mnóstwo i to trudnych do wyłapania błędów jest powodowane przez brak uwagi na referencję.
Jeżeli jednak zależy nam, żeby skopiować tablicę robimy to tak:
var myArray = [
{'first' : 0, 'second': 1},
{'test': 2},
{'name' : 'theme'}
];
var mySecondArray = $A(myArray);
mySecondArray[0].first = 111;
// myArray[0].first = 0, mySecondArray[0].first = 111;
Wszystko pięknie gra.

Podsumowanie

Z klasą Array i jej metodami trzeba się oswoić bo to jedna z częściej wykorzystywanych elementów. Trzeba również pamiętać, że jest pewna pula funkcji, które nie są tutaj ujęte bo są tak niezbędne, że obsługują je wszystkie przeglądarki. Są też na tyle nowe, że dostępne w niewielu przeglądarkach, a ludzie z mootools uznali, że nie są konieczne lub po prostu nie zdążyli ich wprowadzić. Podstawowe trzeba poznać w czym może Wam pomóc np. strona Mozilli. Tych nowych, które są dostępne w niewielu przeglądarkach proponowałbym unikać, chyba, że sami potraficie napisać emulujące je odpowiedniki dla pozostałych aplikacji - chyba, że chcecie żeby strona się stawała nieużywalna pod np. IE.

W następnym tygodniu w “MooTools”

Klasa Functions.

Zakres tematyczny: JavaScript, mootools Dodaj komentarz
Komentarze (0) Trackbacks (0)

Brak komentarzy.


Dodaj komentarz


Spam Protection by WP-SpamFree

Brak trackbacków.