Tablice

Wyobraź sobie, że musisz przechować 10 jakiś wartości. Tworzysz więc 10 zmiennych.
I wszystko jest ok do czasu, gdy dostajesz zadanie, by każdą taką zmienną zwiększyć np. o 2. Zaczynają się schody.
A schody te zwiększają się gdy dochodzisz do momentu kiedy nie możesz z góry określić ile masz tych zmiennych, lub gdy tych zmiennych robi się nie 10, a np. 2000 razy tyle.

Tutaj przychodzą z pomocą tablice, które są uporządkowanymi zbiorami zawierającymi jakieś dane. Dość klasycznym porównaniem jest szafa z bibliotecznymi kartami książek. Półka - tablica - przechowuje karty książek - u nas zmienne.

Tworzenie nowej tablicy

Aby stworzyć nową tablicę korzystamy z kwadratowych nawiasów:


const tab = []; //pusta tablica

const tab2 = [1, 2, 3, 4]; //tablica z 4 liczbami

const tab3 = ['Marcin', 'Ania', 'Agnieszka']; //tablica z 3 elementami

const a = "ALA";
const b = 234;
const c = "PIES";

const tab = [a, b, c, "KOT", {...}]; //tablica z 5 elementami. Mogą to być oddzielne zmienne, ale też wartości wpisane w tablicy
tablica

Jak widzisz powyżej, tablica przetrzymuje kolejne elementy. Nie ma znaczenia jakie są to elementy.
Znaczenie ma to, że są one ponumerowane za pomocą tak zwanych indeksów, które numerowane są od 0.
Pierwsza wartość w tablicy ma index 0, druga 1, trzecia 2 i tak dalej, aż do ostatniego indeksu który wynosi długość_tablicy-1 czyli tab.length-1.

Teraz aby pobrać dane elementy z tablicy wystarczy się do nich odwołać przez nazwę tablicy i index, który podajemy w kwadratowych nawiasach:


const tab = ["ala", "ma", "rudego", "kota"];

console.log( tab[0] ); //ala
console.log( tab[1] ); //ma
console.log( tab[2] ); //rudego
console.log( tab[3] ); //kota

Właściwość length

Każda tablica udostępnia nam właściwość length, która określa jej długość (czyli ilość jej elementów).
Dzięki temu możemy poznać nie tylko długość tablicy, ale i index ostatniego elementu oraz w łatwy sposób przeprowadzać pętlę po wszystkich elementach naszej tablicy.


const tab = ['Marcin', 'Ania', 'Agnieszka'];

console.log(tab.length); //3

Aby odwołać się do ostatniego elementu musimy odjąć 1 od liczby elementów (bo indeksowanie jest od 0):


console.log( tab[ tab.length-1 ] ); //Agnieszka

Pętla po tablicy

Jedną z najwspanialszych rzeczy, jakie dają nam tablice to możliwość wykonywania na nich zbiorczych operacji za pomocą pętli.
Aby zrobić pętlę po tablicy skorzystamy z instrukcji:


const tab = ['Marcin', 'Ania', 'Agnieszka'];

for (let i=0; i<tab.length; i++) {
    console.log('licznik pętli: ' + i);
    console.log('element: ' + tab[i]); //tab[0], tab[1]...
}

Widzisz jak się odwołuję do danego elementu tablicy? Skoro licznik "i" zwiększa się od 0 do tab.length-1, to mogę za jego pomocą pobierać kolejne elementy tablicy czyli tab[0], tab[1]... i tak do tab[tab.length-1]

Pamiętaj, że jeżeli pobieramy dany element z tablicy, to to co właśnie pobraliśmy nie jest jakąś magiczną nieosiągalną rzeczą, a zwykłą wartością, do której odwołalibyśmy się normalnie przez nazwę.
Dlatego właśnie po pobraniu elementu z tablicy traktujemy go tak samo jak klasyczną zmienną (bo w zasadzie to to samo, tylko włożone do tablicy):


const myVar = "Marcin";
console.log(myVar + "!"); //Marcin!


const tab = ["Marcin", "Ania"]
console.log(tab[0] + "!"); //Marcin!


const name = tab[1];
console.log(name.toUpperCase()); //ANIA

Dodawanie elementów do tablicy

Aby dodać element do tablicy możemy użyć metody Array.push(element1, element2, ...):


const tab = ["Marcin", "Ania", "Agnieszka"];
tab.push("Piotrek");

console.log(tab); //[Marcin, Ania, Agnieszka, Piotrek]

Drugą metodą jest po prostu ustawianie elementu na danym indeksie:


const tab = ["Marcin", "Ania", "Agnieszka"];

tab[3] = "Piotrek";
//lub
tab[tab.length] = "Piotrek";

console.log(tab); //[Marcin, Ania, Agnieszka, Piotrek]

Jest to mniej bezpieczna metoda, bo możemy nieumyślnie ustawić element w złym miejscu, co może spowodować, że w tabeli pojawią się puste miejsca:


const tab = ["Marcin", "Ania", "Agnieszka"];

tab[6] = "Piotrek";
console.log(tab); //Marcin, Ania, Agnieszka, empty, empty, Piotrek

lub przez przypadek nadpiszemy już istniejącą wartość:


const tab = ["Marcin", "Ania", "Agnieszka"];

tab[2] = "Piotrek";
console.log(tab); //Marcin, Ania, Piotrek

Zanim przejdziemy dalej, spróbujmy wykonać jakiś prosty przykład. Mamy tablicę imion pisanych małymi literami:


const names = ["marcin", "ania", "agnieszka"];

Naszym celem jest dodanie do tej tablicy 2 dowolnych imion, a następnie wypisanie w konsoli wszystkich imion z tablicy tak by były napisane poprawnie - z dużej litery. Spróbuj zrobić to zadanie samodzielnie np. na tej stronie


        const names = ["marcin", "ania", "agnieszka"];

        names.push("piotr"); //tutaj możemy po przecinku, możemy też oddzielnie
        names.push("karol");

        for (let i=0; i<names.length; i++) {
            const name = names[i];
            //pierwszą literę imienia zamieniamy na dużą i dodajemy resztę liter
            console.log( name.charAt(0).toUpperCase() + name.substr(1) );
        }
        

Kolejnym naszym zadaniem będzie podliczenie wszystkich liter w tablicy. Tak samo jak poprzednio - spróbuj wpierw sam.


const words = ["kot", "świnka", "pies", "chomik", "wróbelek", "szczupakko"];

        const words = ["kot", "świnka", "pies", "chomik", "wróbelek", "szczupakko"];
        let sum = 0;

        for (let i=0; i<words.length; i++) {
            sum += words[i].length;
        }

        console.log(sum);
        

Tablice wielowymiarowe

Skoro tablice mogą w sobie trzymać dowolne wartości, mogą także przetrzymywać obiekty, lub... kolejne tablice. Taki twór zwie się tablicami wielowymiarowymi.

Do czego może się to przydać?
Powiedzmy, że chcemy manipulować na rekordach dotyczących osób. Możemy wtedy stworzyć tablicę, w której w każdym indeksie będzie rekord w formie tablicy zawierająca dane osoby:


const tab = [];
tab[0] = ['Marcin' , '183'];
tab[1] = ['Ania' , '173'];
tab[2] = ['Agnieszka' , '170'];

console.log('imię: ' + tab[0][0] + ', wzrost: ' + tab[0][1]);
console.log('imię: ' + tab[1][0] + ', wzrost: ' + tab[1][1]);
console.log('imię: ' + tab[2][0] + ', wzrost: ' + tab[2][1]);

Poza powyższymi podstawami, tablice udostępniają nam dużą liczbę metod, które możemy na nich używać. Poniżej poznamy te najczęściej używane.

Dodawanie i usuwanie elementów na końcu talicy

Metoda Array.push(el1, el2*...) wstawia nowy element (lub kilka) do tablicy na jej końcu i zwraca długość nowej tablicy. Elementów wstawianych można podać kilka lub jeden.


const tab = ['Marcin', 'Ania', 'Agnieszka'];
tab.push('Grzegorz'));
tab.push('Piotr', 'Karol'));

console.log(tab) //[Marcin, Ania, Agnieszka, Piotr, Karol]

Metoda pop() w przeciwieństwie do push() zabiera ostatni element z tablicy i go zwraca:


const tab = ['Marcin', 'Ania', 'Agnieszka'];
const elDeleted =  tab.pop();

console.log(elDeleted); //Agnieszka
console.log(tab); //[Marcin, Ania]

Dodawanie i usuwanie elemtów na początku tablicy

Metoda unshift(el1, el2*...) wstawia nowy element do tablicy na jej początku, po czym zwraca nową długość tablicy. Elementów wstawianych można podać kilka, albo jeden.


const tab = ['Marcin', 'Ania', 'Agnieszka'];
tab.unshift('Bartek');
tab.unshift('Piotrek', 'Paweł');

console.log(tab); //[Piotrek, Paweł, Bartek, Marcin, Ania, Agnieszka]

Metoda shift() natomiast usuwa pierwszy element z tablicy i zwraca jego wartość:


const tab = ['Marcin', 'Ania', 'Agnieszka'];
const elDeleted = tab.shift();

console.log(tab); //[Ania, Agnieszka]
console.log(elDeleted); //Marcin

Łączenie elementów tablicy przy pomocy metody join()

Metoda Array.join(separator) służy do łączenia kolejnych elementów w jeden tekst. Opcjonalnym parametrem tej metody jest separator - tekst, który będzie oddzielał kolejne elementy w utworzonym tekście. Jeżeli go nie podamy będzie użyty domyślny znak przecinka:


const ourTable = ["Marcin", "Ania", "Agnieszka"];

console.log(ourTable.join()); //wypisze się "Marcin,Ania,Agnieszka"

console.log(ourTable.join(" - ")); //wypisze się "Marcin - Ania - Agnieszka"

console.log(ourTable.join(" <--> ")); //wypisze się "Marcin <--> Ania <--> Agnieszka"

Do czego to może się przydać?

Wyobraź sobie, że masz kilka samochodów. Chciałbyś je wszystkie wypisać, stawiając między nimi przecinki. Wystarczy więc użyć pętli:


const cars = ["Mercedes", "Audi", "BMW"];

let str = '';
for (let i=0; i<cars.lengthl i++) {
    str += cars[i] + ', ';
}
console.log(str); //"Mercedes, Audi, BMW, "

Problem z powyższym rozwiązaniem jest taki, że na końcu tekstu też dostajesz przecinek i spację. Musiałbyś więc w pętli sprawdzać czy dany element jest ostatni i w razie czego nie dodawać tych znaków.
Mógłbyś też po prostu użyć join:


const cars = ["Mercedes", "Audi", "BMW"];
console.log( cars.join(', ') ); //"Mercedes, Audi, BMW"

Niektórzy też wykorzystują join() do wypisywania tekstu wielolinijkowego. Jeżeli nie korzystasz z template strings, pisanie wielolinijkowego stringa może być męczące. Można to uprościć za pomocą join():


//zamiast
let text = "Ala ma kota<br>"
text += "a kot ma Alę<br>"
text += "Ala go kocha<br>"
text += "a kot ją wcale"
console.log(text);

//można
const text = ["Ala ma kota", "a kot ma Alę", "Ala go kocha", "a kot ją wcale"];
console.log(text.join("<br>"));

Odwracanie kolejności elementów tablicy przy pomocy metody reverse()

Dzięki metodzie reverse() możemy odwrócić elementy naszej tablicy:


const tab = ["numer 1" , "numer 2" , "numer 3" , "numer 4"];

console.log('Przed: ' + tab); //Przed: numer 1,numer 2,numer 3,numer 4
tab.reverse()
console.log('Po: ' + tab); //Po: numer 4,numer 3,numer 2,numer 1

Sortowanie tablic za pomocą metody sort()

Metoda Array.sort(fn) służy do sortowania tablic.
Metoda ta przekształca równocześnie tablicę, na której została wykonana.

Powiedzmy, że mamy tablicę z niepoukładanymi wartościami, i chcemy ją posortować:


const tab = ['Marcin', 'Ania', 'Piotrek', 'Grześ'];
tab.sort();
console.log( tab ); //wypisze się "Ania, Grześ, Marcin, Piotrek"

Standardowo Javascript segreguje tablice leksykalnie (czyli tak jak w książce telefonicznej) traktując liczby jak litery.
Powoduje to w wielu przypadkach nieoczekiwane rezultaty - na przykład 1200 będzie mniejsze od 300 (bo liczy się pierwsza cyfra), 1.3 będzie większe od 13 itp.

Aby móc posegregować naszą tablicę według własnych kryteriów, musimy skorzystać z dodatkowego parametru, który metoda sort() może opcjonalnie przyjmować.
Parametr ten jest nazwą naszej funkcji sortującej (lub funkcją anonimową, której też możemy użyć). Do funkcji tej przekazane zostaną 2 parametry - dwa kolejne porównywane ze sobą elementy.

Pisząc taką funkcję musimy pamiętać o 3 warunkach:

  • Jeżeli funkcja(a, b) zwróci wartość mniejszą od 0, to wartość a będzie miała mniejszy index od b
  • Jeżeli funkcja(a, b) zwróci 0, to indexy wartości a i b pozostaną bez zmian
  • Jeżeli funkcja(a, b) zwróci wartość większą od 0, to wartość a będzie miała większy index od b

Tak więc ogólna postać funkcji sortującej wygląda tak:


function porownaj(A, B) {
    if (A jest mniejsze od B) {return -1}
    if (A jest większe od B) {return 1}
    //jeżeli A równa się B
    return 0
}

Przykładowo aby posegregować wartości liczbowe musimy utworzyć funkcję:


function compareNr(a, b) {
    return a - b
}

const tab = [100, 320, 10, 25, 310, 1200, 400];

const tab2 = tab.sort();
console.log( tab2 ); //10,100,1200,25,310,320,400

const tab3 = tab.sort(compareNr);
console.log( tab3 ); //10,25,100,310,320,400,1200

Oczywiście możemy sami wymyślać nasze własne kryteria segregacji (na przykład by cyfry były "mniejsze" od liter, lub też duże litery były "mniejsze" od małych liter...).

Spróbujmy więc posortować tablicę z poprzednich przykładów względem wieku osób:


const tab = [];
tab[0] = ['Marcin' , 183];
tab[1] = ['Ania' , 173];
tab[2] = ['Agnieszka' , 170];

//dla sort spokojnie możemy używać funkcji anonimowej
const tab2 = tab.sort(function(a, b) {
    return a[1] - b[1];
});

console.log(tab2);

Skoro już umiemy łączyć tablicę i ją sortować, możemy to wykorzystać w klasycznym przykładzie. Wyobraź sobie, że masz tekst, w którym chcesz uporządkować słowa:


const text = "Marcin Ania Basia Patryk Beata Aneta";

const namesArr = text.split(' '); //korzystamy z metody, która dzieli tekst za pomocą parametru w wyniku dostajemy tablicę
namesArr.sort();

const textSorted = namesArr.join(' '); //łączymy posortowaną tablicę
console.log(textSorted); //"Aneta Ania Basia Beata Marcin Patryk"

Łączenie dwóch tablic

Do połączenia dwóch tablic nie możemy użyć zwykłego dodawania (tak samo nie możemy odejmować tablicy od tablicy):


const tab1 = ["Ala", "Basia"];
const tab2 = ["Piotr", "Marcin"];
console.log(tab1 + tab2); //Ala,BasiaPiotr,Marcin

Wynikiem jest string. Zadziałała tutaj automatyczna konwersja typów z wykorzystaniem metody toString(). Tak czy siak wynik jest błędny.

Aby połączyć ze sobą kilka tablic wykorzystamy metodę Array.concat(array1, array2...), która jako parametr przyjmuje jedną lub kilka tablic:


const animals = ["Pies", "Kot"];
const animals2 = ["Słoń", "Wieloryb"];
const animals3 = ["Chomik ninja", "Świnka morderca"];

const newTableSmall = animals.concat(animals2);
const newTableBig = animals.concat(animals2, animals3);

console.log(newTableSmall); //wypisze ["Pies", "Kot", "Słoń", "Wieloryb"]
console.log(newTableBig); //wypisze ["Pies", "Kot", "Słoń", "Wieloryb", "Chomik ninja", "Świnka morderca"]

Wycinanie kawałka tablicy za pomocą slice()

Metoda Array.slice(od, do*) tak samo jak przy stringach, zwraca nową tablicę zawierającą wycięte elementy z tablicy na której została wywołana. Metoda ta nie modyfikuje tablicy, na której jest wywoływana.

Pierwszy parametr od wskazuje na index, od którego ma "wyciąć" elementy, a do analogicznie wskazuje indeks do jakiego będziemy ciąć. Jeżeli go nie podamy, zostanie wycięty kawałek od danego indeksu do końca tablicy.


const tab = ['Marcin', 'Ania', 'Agnieszka'];

const tab2 = tab.slice(0, 1)
console.log(tab2); //wypisze się "Marcin"
console.log(tab); //wypisze się "Marcin, Ania, Agnieszka"


const tab3 = tab.slice(0,2)
console.log(tab3); //wypisze się "Marcin, Ania"

const tab4 = tab.slice(0,5)
console.log(tab4); //wypisze się "Marcin, Ania, Agnieszka"

const tab5 = ourTable.slice(0,1)
console.log(tab5); //wypisze się "Marcin"

Usuwanie lub dodawanie elementów za pomocą metody splice()

Metoda splice(index, ileUsunąć, noweElementy*...) służy zarówno do usuwania jak i wstawiania nowych elementów do tablicy. Parametr index określa miejsce w tablicy od którego zacznie się usuwanie elementów.
Parametr ileUsunąć mówi ile elementów powinno być usuniętych z tablicy. Jeżeli podamy 0, wtedy nic nie zostanie z tablicy usunięte.
Opcjonalne parametry elementyWstawiane to elementy, które będą wstawiane tuż przez usuwanymi elementami, lub tuż przed danym elementem, jeżeli nic nie usuwamy.


const tab = ['Marcin', 'Ania', 'Agnieszka'];

tab.splice(1, 1); //od indexu 1 usuwam 1 element
console.log(tab); //wypisze "Marcin, Agnieszka"

tab.splice(1, 0, 'Monika', 'Magda') //nic nie usuwam i wstawiam nowe elementy przed index 1
console.log(tab); //wypisze "Marcin, Monika, Magda, Agnieszka"

Usuwanie dowolnego elementu tablicy

Aby usunąć jakiś element w tablicy wykorzystamy powyżej opisaną metodę splice:


const tab = ['Marcin', 'Ania', 'Agnieszka', 'Monika'];
const deletedEl = tab.splice(2, 1);
console.log(tab); //wypisze 'Marcin', 'Ania', 'Monika'
console.log(deletedEl); //wypisze 'Agnieszka'

Dodanie elementu do tablicy w dowolnym miejscu

Tutaj tak samo musimy skorzystać z metody splice:


const tab = ['Marcin', 'Ania', 'Agnieszka', 'Monika'];
tab.splice(1, 0, 'Piotr'); //1 - który index, 0 - ile usunąć, 'Piotr' - element dodawany przed danym indexem
console.log(tab); //[Marcin, Piotr, Ania, Agnieszka, Monika]

Wyszukiwanie elementu w tablicy

Wyszukać element w tablicy możemy na kilka sposobów.

Pierwszy z nich polega (podobnie jak w przypadku przeszukiwania stringów) na skorzystaniu z metody Array.indexOf(str).
Metoda ta działa tak samo jak przy tekstach. Zwraca index na którym znalazła szukany tekst, lub -1, jeżeli nic nie znalazła:


const tab = ['Marcin', 'Ania', 'Agnieszka', 'Monika'];

//inaczej
if (tab.indexOf('Ania') !== -1) {
    console.log('Ania występuje w tablicy pod indexem ' + tab.indexOf('Ania'));
}

Metoda indexOf sprawdza się tylko przy tablicy tekstów. A co jeżeli chcemy szukać w tablicy której dane są bardziej skomplikowane - np. są obiektami? Moglibyśmy wykonać pętlę for, ale o wiele wygodniejsze jest skorzystać z automatów.
Aby coś wyszukać w tablicy skorzystamy z metody Array.find(fn(el, i*, array*), this*)


const tab = [
    { name : 'Marcin', age: 18 },
    { name : 'Ania', age: 16 },
    { name : 'Agnieszka', age: 16}
];

//metoda find zwraca pierwszy element pasujący do równania
const findUser = tab.find(function(el) {
    return el.age > 15
});

console.log(findUser) //{name : 'Ania', age: 16 }

Funkcja ta pojawiła się w EcmaScript 2015 (ES6). Wymaga tylko jednego parametru - funkcji, do której będą przekazywane kolejne elementy z tablicy (w przykładzie powyżej elementy te trafią każdorazowo pod zmienną el). Jeżeli funkcja w którejś iteracji zwróci true, znaczy to, że badany element spełnia założenia i zostanie on zwrócony.


const tab = [12, 5, 8, 130, 44];

const bigNr = tab.find(function(el) {
    return el >= 15
});

console.log(bigNr); //130

Pętla po tablicy za pomocą for of

Wiesz, że po tablicy możesz wykonać klasyczną pętlę:


const tab = ['Marcin', 'Ania', 'Agnieszka', 'Piotrek', 'Grześ', 'Magda'];

for (let i=0; i<tab.length; i++) {
    console.log(tab[i]);
}

Można powiedzieć, że pętla for jest typowo "manualna". Trzeba ustawić licznik, kiedy się kończy, jak ma być zwiększany.

Do podobnych iteracji po tablicy możemy użyć pętli for of, która została wprowadzona w ES6 i służy do iterowania po elementach iterowalnych (stringi, kolekcje, tablice itp):


const tab = [1,2,3,4];
for (const el of tab) {
    console.log(el);
}

const buttons = document.querySelectorAll('button');
for (const btn of buttons) {
    console.log(btn);
}

const txt = "ALa ma kota";
for (const i of txt) {
    console.log(i.toUpperCase());
}

Pętla po tablicy za pomocą forEach()

Pierwszą z omawianych metod jest Array.forEach(fn(el*, i*, array*)

Metoda ta przyjmuje jako pierwszy parametr funkcję, do której będzie przekazywać 3 właściwości:
- dany element z tablicy (opcjonalny),
- aktualny licznik pętli (opcjonalny)
- aktualną tablicę (opcjonalny)

Drugi opcjonalny parametr forEach wskazuje na aktualne "this", które czasami może się zmienić. Co to takiego? Dowiesz się o tym więcej w dziale o obiektach.


const tab = ['Marcin', 'Ania', 'Agnieszka'];


tab.forEach(function() => {
    console.log("Wykonam się tyle razy ile jest elementów w tablicy");
});

//pod zmienną el trafią kolejne elementy to jest Marcin, Ania i Agnieszka
tab.forEach(function(el) {
    console.log(el.toUpperCase());
});

//pod zmienną i będzie wstawiany licznik
tab.forEach(function(el, i) => {
    console.log(el, i);
});

//pod zmienną arr wstawiana będzie aktualna tablica po której iterujemy - może kiedyś to się przyda?
tab.forEach(function(el, i, arr) => {
    console.log(el, i, arr);
});


//prosty przykład
let sumLetter = 0;
tab.forEach(function(el) {
    sumLetter += el.length;
});

console.log("Liczba liter wszystkich imion to: " + sumLetter); //19

Poza funkcją istnieje dla forEach jeszcze drugi parametr, który służy do ustawiania this. Domyślnie wewnątrz forEach (ale także i map, every, some, filter itp) this wskazuje na obiekt window:


const tab = ['Marcin', 'Ania', 'Agnieszka'];

tab.forEach(function() => {
    console.log(this); //window
});


const ob = {name : "Marcin"};

tab.forEach(function() => {
    console.log(this); //ob
}, ob);

Temat ten będzie bardziej poruszany w dziale związanym z obiektami. W większości przypadków parametr ten będzie pomijany.

Sprawdzanie elementów w tablicy za pomocą every() i some()

Metody Array.every(fn(el, i*, array*) i Array.some(fn(el, i*, array*) służą do sprawdzania czy wszystkie lub czy kilka elementów w tablicy spełnia dany warunek.
Można je przyrównać do && i || w instrukcjach warunkowych.

Metoda every() zwróci prawdę, kiedy każdy element tablicy będzie pasował do danego warunku (czyli tak jak && w ifach).

Metoda some() zwróci prawdę, jeżeli dowolny z elementów będzie pasował do przyrównania (czyli ||).

Obie metody przyjmują takie same parametry jak forEach.


const tab = [1,3,5,8,9];

//sprawdzam czy wszystkie liczby są parzyste
const allEven = tab.every(function(el) {
    return el % 2 === 0;
}); //false


//sprawdzam czy niektóre liczby są parzyste
const someEven = tab.some(function() {
    return el % 2 === 0;
}); //true

const tab = [
    { name : 'Marcin', age: 18 },
    { name : 'Ania', age: 16 },
    { name : 'Agnieszka', age: 16}
];

//czy wszyscy użytkownicy są pełnoletni?
const isAllOfAge = tab.every(function(el) {
    return el.age >= 18;
});

console.log(isAllOfAge); //false


//a może tylko niektórzy?
const isSomeOfAge = tab.some(function(el) {
    return el.age >= 18
});

console.log(isSomeOfAge); //true

Mapowanie tablicy za pomocą map()

Metoda map() robi pętlę po tablicy i każdorazowo zwraca nowy element tablicy. W wyniku po zakończeniu całej pętli zwracana jest nowa tablica z taką samą liczbą elementów jaką miała tablica na której ta metoda była wykonana:


const tab = ['Marcin', 'Ania', 'Agnieszka'];

const tab2 = tab.map(function(el) {
    return el.toUpperCase()
});

console.log(tab); //[Marcin, Ania, Agnieszka]
console.log(tab2); //[MARCIN, ANIA, AGNIESZKA]

const tab = [1, 2, 3];
const tab2 = tab.map(function(el) {
    return el * 2;
});

console.log(tab2); //[2, 4, 6]

const numbers = [1.2, 4.5, 9.3];

const absolute = numbers.map(function(el) {
    return Math.ceil(el);
});
console.log(absolute); //[2, 5, 10]

function x3(number) {
    return number * 3;
}

var ourTable = [1, 2, 3];
console.log(ourTable.map(x3)); //[3, 6, 9]

Oglądając tutoriale na necie z pewnością zauważysz, że autorzy stosują map jako zamiennik dla forEach. Jeżeli rezultat działania tej pętli i tak nie zostanie podstawiony pod żadną zmienną, działanie obydwu funkcji jest w zasadzie podobne:


const tab = ['Marcin', 'Ania', 'Agnieszka'];

tab.forEach(function(el) {
    console.log(el.toUpperCase());
});

//działanie podobne, ale zyskaliśmy 4 litery zapisu...
tab.map(function(el) {
    console.log(el.toUpperCase());
});

Filtrowanie elementów za pomocą filter()

Bardzo często będziemy chcieli przefiltrować daną tablicę zwracając tylko elementy, które pasują do danego warunku. Bardzo przydatną funkcją jest tutaj funkcja Array.filter(fn).
Funkcja filter() robi pętlę po danej tablicy i w wyniku zwraca nową tablicę, która zawiera wszystkie elementy, przy których zostało zwrócone true:


const ourTable = [1, 2, 3, 4, 5, 6];
const evenNumbers = ourTable.filter(function(el) {
    return el % 2 === 0;
});

console.log(evenNumbers); //[2, 4, 6]

const ourTable = ['Marcin', 'Ania', 'Agnieszka', 'Monika', 'Piotrek'];
const woman = ourTable.filter(function(el) {
    return el.charAt(el.length-1) === 'a';
});

console.log(woman); //["Ania", "Agnieszka", "Monika"]

Redukowanie tablicy za pomocą reduce

Za pomocą funkcji reduce(function(prev, next, i*, arr*) {}, start*) możemy wykonywać operacje na tablicy "redukując ją", w wyniku uzyskując jakiś wynik.

Działanie tej funkcji jest następujące: w pierwszej iteracji pod pierwszy parametr prev wstawiany jest pierwszy element tablicy, a pod next drugi. Funkcja zwraca jakiś wynik. W kolejnej iteracji jest od podstawiany pod prev, a kolejny element w tablicy pod next. Znowu zostaje zwrócony wynik, który w kolejnej iteracji wstawiany jest w miejsce prev, a pod next znowu wstawiany jest kolejny element tablicy.

Poza pierwszym parametrem, która jak w powyższych metodach jest naszą funkcją, możemy też podać początkową wartość, od której zostanie rozpoczęte liczenie (można powiedzieć, że w 0 iteracji pod prev zostanie wstawiony ta startowa wartość, a pod next wstawiony zostanie pierwszy element tablicy)


const tab = [1, 2, 3, 4];
const result = tab.reduce(function(prev, next) {
    return prev + next;
});
//1 iteracja => prev = 1, next = 2
//2 iteracja => prev = 3, next = 3
//3 iteracja => prev = 6, next = 4
//wynik = 10


//atrybut po funkcji to początkowa wartość wyniku
const sum1 = [1, 2, 3].reduce(function(prev, next) {
    return prev + next;
}, 0);
//sum1 = 6


//atrybut po funkcji to początkowa wartość wyniku
const sum2 = [1, 2, 3].reduce(function(prev, next) {
    return prev + next;
}, "");
//sum1 = "123"


//parametry oczywiści mogą nazywać się inaczej
const sum3 = [1, 2, 3].reduce(function(a, b) {
    return a + b + '.';
}, '.');
//sum2 = ".1.2.3."


const data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const flatArray = data.reduce(function(total, amount) {
  return total.concat(amount);
}, []);
//flatArray = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]


const data2 = [1,2,3,4];
const data2.reduce(function(a, b, i) {
    return (a + b) * i;
});
//(1 + 2) * 1  = 3,
//(3 + 3) * 2  = 12,
//(12 + 4) * 3 = 48

Łańcuchowość metod

Method chaining (łańcuchowość metod) to sposób odpalania kolejnych metod (funkcji), które zapisujemy po kropce. Technika ta tyczy się nie tylko tablic, ale całego JavaScript.

Każda metoda czy właściwość daje jakąś wartość. Jeżeli taka zwrócona wartość pasuje dla kolejnej metody, możemy tą metodę od razu odpalić po kropce:


const text = "Ala ma kota";

text.toUpperCase().substr(0, 3).length //kolejne metody odpalamy po kropce

//czasami trzymanie wszystkiego w jednej linii nie jest dobrym rozwiązaniem
text
    .toUpperCase()
    .substr(0, 3)
    .length

Podobnie jest z powyższymi metodami dotyczącymi tablic. Jeżeli dana metoda zwraca tablicę (np. map czy filter zwracają tablicę) możemy od razu po nich odpalać kolejne metody.


const tab = ['Marcin', 'Ania', 'Agnieszka'];


const newTab = tab.map(function(el) { //zwracam nową tablicę...
    return el.toUpperCase()
}).filter(function(el) { //więc mogę ją odfiltrować
    return el[el.length-1].toUpperCase() === "A"
}).map(function(el) { //map też może służyć do iteracji
    return el + "!";
}).forEach(function(el) {
    console.log(el);
}) //forEach nie zwraca tablicy więc nie mogę już tutaj działać jak na tablicy

console.log(newTab)

Powyższy przykład można jeszcze bardziej uprościć stosując zamiast funkcji anonimowych referencje do funkcji. Dzięki temu nie tylko nasz zapis się upraszcza, ale i zyskujemy dodatkowe "klocki" do przyszłego wykorzystania.


const tab = ['Marcin', 'Ania', 'Agnieszka'];

const upper = function(el) {
    return el.toUpperCase();
}

const checkIsWoman = function(el) {
    return el[el.length-1].toUpperCase() === "A"; //naiwny test
}

const addExclamationMark = function(el) {
    return el + "!"
}

const newTab = tab
                .map(upper)
                .filter(checkIsWoman)
                .map(addExclamationMark);

console.log(newTab)

A powyższy zapis jeszcze bardziej się uprości, jeżeli zastosujemy funkcję strzałkową


const tab = ['Marcin', 'Ania', 'Agnieszka'];

const upper = el => el.toUpperCase();
const checkIsWoman = el => el[el.length-1].toUpperCase() === "A"; //naiwny test
const addExclamationMark = el => el + "!"

const newTab = tab
                .map(upper)
                .filter(checkIsWoman)
                .map(addExclamationMark);

console.log(newTab)

Wyliczanie średniej wartości elementów tablicy

JavaScript nie udostępnia nam metody do wyliczania średniej w tablicy. I bardzo dobrze. Język powienien być optymalny, a nie dostosowany do każdej możliwości. Na szczęście wyliczenie takiej średniej jest całkiem łatwe.
Zasada bardzo prosta - dodaj wszystkie elementy tablicy i podziel je przez ilość tych elementów:


function tableAverage(arr) {
    let sum = 0;
    for (let i=0; i<arr.length; i++) {
        sum += arr[i];
    }

    return sum / arr.length;
}

Poniżej poznasz metodę reduce, dzięki której powyższe zadanie można zapisać jeszcze krócej:


const tab = [1,2,3,4,5];
const avg = tab.reduce(function(a,b) {
    return a + b;
}) / tab.length;

Co w połączeniu z funkcją strzałkową z ES6 daje nam:


const tab = [1,2,3,4,5];
const avg = tab.reduce((a,b) => a + b) / tab.length;

Największa i najmniejsza wartość w tablicy

Tu także musimy skorzystać z własnego intelektu.
Jak sprawdzić która wartość w tablicy jest największa? Podstawiamy pod zmienną max pierwszy element tablicy. Wykonujemy pętlę po pozostałych elementach tablicy. Jeżeli dany element jest większy od zmiennej max, to pod tą zmienną podstawiamy wartość tego elementu. I tak do końca tablicy... Tak samo czynimy w przypadku znajdowania najmniejszej wartości. Różnica polega na sprawdzaniu, czy wartość sprawdzanego elementu jest mniejsza od zmiennej mins


function tableMax(arr) {
    let max = arr[0];
    for (let i=1; i<arr.length; i++) {
        max = (arr[i] > max)? arr[i] : max;
    }
    return max;
}

function tableMin(arr) {
    let min = arr[0];
    for (let i=1; i<arr.length; i++) {
        min = (arr[i] < min)? arr[i] : min;
    }
    return min;
}


const tab = [2, 1, 2, 3, 6, 7, 3];
console.log( tableMax(tab) );
console.log( tableMin(tab) );

Powyższe przepisy są bardzo proste, co nie znaczy, że najbardziej optymalne. Inne ciekawe podejścia do wyszukiwania najmniejszej i największej wartości w tablicy znajdziesz na stronie: http://stackoverflow.com/questions/1669190/javascript-min-max-array-values

Przykładowo można taką tablicę posortować i pobrać pierwszą lub ostatnią (w zależności od sposobu sortowania) liczbę:


const tab = [2, 1, 2, 3, 6, 7, 3];

tab.sort(function(a, b) {
    return a - b;
});

console.log(tab[tab.length - 1]); //7

Innym ciekawym rozwiązaniem jest skorzystanie z spread operator lub bardziej zaawansowanych zapisów jak apply, możemy powyższe zadania zrealizować o wiele krócej:


const tab = [2, 1, 2, 3, 6, 7, 3];

//spread - ES6
const min = Math.min(...tab);
const max = Math.max(...tab);

//apply - ES5
const min = Math.min.apply(null, tab);
const max = Math.max.apply(null, tab);

Trening czyni mistrza

Poniżej zamieszczam kilka zadań, które w ramach ćwiczenia możesz wykonać:

  1. Zadeklaruj tablicę tab z 5 tekstami. Wypisz w konsoli tą tablicę i jej długość.
    
                    const tab = ["Kot", "Pies", "Chomik", "Ninja", "Wiewiórka"];
                    console.table(tab);
                    console.log(tab.length);
                
  2. Robiąc pętlę po tablicy z pierwszego zadania wypisz każdy element oraz jego długość. Skorzystaj z pętli for, for of i forEach
    
                for (let i=0; i<tab.length; i++) {
                    console.log(tab[i], tab[i].length);
                }
    
                for (const el of tab) {
                    console.log(el, el.length);
                }
    
                tab.forEach(function(el) {
                    console.log(el, el.length);
                });
                
  3. Do tabeli z pierwszego zadania dodaj na końcu i początku po 1 nowym elemencie. Wykorzystaj odpowiednie metody. Po dodaniu elementów wypisz długość tablicy.
    
                tab.push("Kitket");
                tab.unshift("Pet");
    
                console.log(tab.length);
                
  4. Stwórz tablicę 10 losowych absolutnych liczb z przedziału 1-10. Wykorzystaj do tego odpowiedni wzór ze strony strony. Po utworzeniu tablicy wypisz w konsoli jej największy i najmniejszy element. Uwaga! Nie stosuj do tego pętli for jak w przykładach powyżej, a metodę sort()
    
                const tab = [];
                for (var i=0; i<10; i++) {
                    tab.push(  Math.floor(Math.random() * 10) + 1);
                }
    
                tab.sort(function(a, b) {
                    return a-b;
                });
    
                console.log("Tablica: ", tab);
                console.log("Najmniejszy element: ", tab[0]);
                console.log("Największy element: ", tab[tab.length-1]);
                
    lub w ES6
    
                const tab = [];
                for (var i=0; i<10; i++) {
                    tab.push(  Math.floor(Math.random() * 10) + 1);
                }
    
                console.log("Tablica: ", tab);
                console.log("Najmniejszy element: ", Math.min(...tab));
                console.log("Największy element: ", Math.max(...tab));
                
  5. Masz tablicę użytkowników:
    
                const tabUsers = [
                    {name : "Marcin", age: 14},
                    {name : "Piotr", age: 18},
                    {name : "Agnieszka", age: 13},
                    {name : "Weronika", age: 20}
                ]
            
    Wypisz w konsoli użytkowników, którzy są pełnoletni.
    
                console.log(
                    tabUsers.filter(function(user) {
                        return user.age >= 18
                    })
                )
                
  6. Ostatnie zadanie dla [prawdziwych webmasterów] tutaj: tablice-zadanie.html
    
                let tekst = '';
    
                //tutaj zrob odpowiednie petle - np. for:
                for (let y=0; y<tab.length; y++) {
                    const row = tab[y];
                    for (let x=0; x<row.length; x++) {
                        const pobranyKolor = colors[row[x]];
                        tekst += '<div style="background:' + pobranyKolor + '"></div>';
                    }
                    tekst += '<br>';
                }
    
                //tutaj wstawiamy do div wygenerowany html - nie ruszaj poniższej linijki
                document.querySelector('.canvas').innerHTML = tekst;