W tym epizodzie poznamy wszystkie warte uwagi metody do pracy na tablicach, nie licząc map, filter oraz reduce, które doczekały się osobnej lekcji. Wyjdziemy poza „push” i „pop”, nauczymy się dodawać i usuwać elementy w dowolnym miejscu, sortować tablice, poznamy także pewne tablicowe nowości jak toSorted czy toSpliced.
Przypomnienie – push, pop, length
Metoda push dodaje elementy do tablicy. Warto zwrócić uwagę, że ona także coś zwraca – aktualną długość tablicy:
const animals = ['pigs', 'goats', 'sheep'];
const count = animals.push('cows');
console.log(count);
//4
console.log(animals);
//["pigs", "goats", "sheep", "cows"]
Metoda pop usuwa ostatni element z tablicy, jak również go zwraca:
const plants = ['broccoli', 'cauliflower', 'cabbage', 'kale', 'tomato'];
console.log(plants.pop());
//"tomato"
console.log(plants);
//["broccoli", "cauliflower", "cabbage", "kale"]
plants.pop();
console.log(plants);
//["broccoli", "cauliflower", "cabbage"]
Listy w JavaScript mają też właściwość length, która zawiera, jak się można domyślić, ilość elementów w tablicy:
const clothing = ['shoes', 'shirts', 'socks', 'sweaters'];
console.log(clothing.length);
//4
Shift, unshift – dodawanie i usuwanie po lewej
Metoda shift to przeciwieństwo methody push – dodaje elementy, ale z lewej strony, na początku tablicy:
let fruits = ["Apple", "Banana", "Orange"];
let shiftedFruit = fruits.shift();
console.log(shiftedFruit);
//"Apple"
console.log(fruits);
//["Banana", "Orange"]
Metoda unshift to poniekąd przeciwieństwo pop – usuwa element, ale z początku tablicy. Zwraca natomiast nową długość tablicy:
let fruits = ["Banana", "Orange"];
let newLength = fruits.unshift("Apple", "Mango");
console.log(newLength);
//4
console.log(fruits);
//["Apple", "Mango", "Banana", "Orange"]
Kopia wycinka – metoda slice
Metoda slice tworzy kopię wycinka tablicy. Tak to najlepiej można ująć.
Sposób na skopiowanie wycinka tablicy od indeksu 1 do indeksu 3 (bez indeksu 3):
let array = [1, 2, 3, 4, 5];
let slicedArray = array.slice(1, 3);
console.log(slicedArray); //[2, 3]
Możemy też przekopiować wszystko do naszej nowej tablicy:
let array = [1, 2, 3, 4, 5];
let newArray = array.slice();
console.log(newArray);
//[1, 2, 3, 4, 5]
Miejsce startowe możemy wyznaczać nie tylko liczbami dodatnimi (np. 2 to „od indeksu drugiego”) ale także liczbami ujemnymi. Podanie liczby -2 będzie oznaczało „skopiuj mi wycinek od drugiego miejsca od końca”:
let array = [1, 2, 3, 4, 5];
let endSlice = array.slice(-2);
console.log(endSlice);
//[4, 5]
Splice – osobliwa metoda in-place
O splice musimy jedynie pamiętać, że jest to metoda in-place, czyli taka, która modyfikuje oryginalną tablicę. Poza tym – jest tak pokręcona, że jeżeli będziemy z niej korzystać, będziemy sprawdzać co jak działa w dokumentacji.
Ale zobaczmy kilka przykładów:
let array = [1, 2, 3, 4, 5];
let removed = array.splice(1, 2);
console.log(array); //[1, 4, 5]
console.log(removed); //[2, 3]
Jak widać, splice dokonało modyfikacji. Zaczynając od indeksu 1 usunęło 2 elementy. Także zwróciło te 2 elementy, zapisane w zmiennej removed.
Pierwszy argument – gdzie zaczynamy (indeks). Drugi argument – ile usuwamy elementów.
Drugi przykład:
let array = [1, 4, 5];
array.splice(2, 0, 6, 7);
console.log(array); //[1, 4, 6, 7, 5]
Pierwszy argument oznacza – zacznij od indeksu 2. Drugi ustawiony na 0 – nie usuwaj niczego.
Dalej mamy 6 i 7, zwykłe liczby. Liczby, które będziemy dodawać zaczynając od indeksu 2.
I mamy naszą tablicę. Po drugim indeksie, niczego nie usuwając, dodaliśmy sobie 6 i 7.
Możemy sprawę sobie jeszcze utrudnić, ciągnąc dalej ten przykład:
let array = [1, 4, 6, 7, 5];
array.splice(1, 2, 8, 9);
console.log(array); // [1, 8, 9, 7, 5]
Zaczynając od indeksu 1, usuwamy 2 elementy, dodajemy 2 elementy, dalej tablica bez zmian.
Nie musimy z tej funkcji korzystać, ale istnieje taka i grzechem byłoby, gdybym o niej nie wspomniał.
Sortowanie tablic – metoda sort
Metoda sort sortuje tablicę. Modyfikuje ją, jest zatem metodą in-place. Jeżeli chodzi o sortowanie alfabetyczne, sama wie, jak to robić:
let fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.sort();
console.log(fruits);
//["Apple", "Banana", "Mango", "Orange"]
Jeżeli chodzi o inne typy danych – musimy przekazać jej jakąś funkcję:
let numbers = [40, 100, 1, 5, 25, 10];
numbers.sort((a, b) => a - b);
console.log(numbers);
//[1, 5, 10, 25, 40, 100]
Może się to wydawać trudne, ale działa to tak – wynik funkcji dodatni, element idzie do przodu. Wynik funkcji ujemny – element zostaje.
100 – 1 to 99, liczba dodatnia, 100 idzie do przodu.
1 – 5 to -4, liczba ujemna, element zostaje. 5 – 25 to -20, liczba ujemna, element zostaje.
Czyli przekazać musimy taką funkcję, która zwraca liczbę, dodatnią bądź ujemną. I porównujemy elementy ze sobą. Gdyby chodziło tylko o liczby, wystarczyłoby zapamiętać patent na sortowanie rosnące i malejące (malejące to b – a).
Problem w tym, że tablica może zawierać różne typy, obiekty nawet, i musimy mieć „patent” w jaki sposób je chcemy sortować. Oto przykład:
const students = [
{ name: "Alex", grade: 15 },
{ name: "Devlin", grade: 15 },
{ name: "Eagle", grade: 13 },
{ name: "Sam", grade: 14 },
];
students.sort((firstItem, secondItem) => firstItem.grade - secondItem.grade);
Sortujemy po ocenach, rosnąco. Generalnie jednak chodzi o jedną rzecz – porównujemy ze sobą 2 elementy. Jeżeli wynik porównania jest pozytywny, element pierwszy zostaje na miejscu. Jeżeli negatywny, element idzie do przodu.
Weźmy dla przykładu taką oto tablicę:
let arr = [1, '2', 3, '4', 5];
Teraz posortować mamy, aby napisy były po prawej, typy liczbowe po lewej.
Co robimy?
let arr = [1, '2', 3, '4', 5];
arr.sort((first, second) => {
if(typeof first === 'number')
return -1;
if(typeof first === 'string')
return 1;
});
console.log(arr);
//[5, 3, 1, '2', '4']
Co tu się stało? Cóż, sprawdzamy, czy dany element jest typem liczbowym. Jeżeli tak – zwracamy -1, czyli stój na miejscu.
Jeżeli element pierwszy jest typem napisowym, zwracamy 1 (liczbę dodatnią) czyli idź do przodu.
Oczywiście logikę sortowania możemy dowolnie tworzyć. Nie jest to najłatwiejsze zagadnienie, ale da się to zrozumieć.
toSorted – sort, który nie jest in-place
Metoda sort jest in-place, sortuje, modyfikuje oryginalną tablicę. Metoda toSorted to taki odpowiednik sort, który zwraca posortowaną tablicę, bez modyfikacji oryginalnej.
const months = ["Mar", "Jan", "Feb", "Dec"];
const sortedMonths = months.toSorted();
console.log(sortedMonths);
// ['Dec', 'Feb', 'Jan', 'Mar']
console.log(months);
// ['Mar', 'Jan', 'Feb', 'Dec']
Tak samo jak sort, może przyjąć funkcję, która określa jak sortować elementy (ten sam mechanizm co w sort):
const values = [1, 10, 21, 2];
const sortedValues = values.toSorted((a, b) => a - b);
console.log(sortedValues); // [1, 2, 10, 21]
console.log(values); // [1, 10, 21, 2]
Zadanie 1 – własna funkcja toSorted
Zadanie łatwiejsze niż się wydaje. Napiszemy własną funkcję toSorted, zwłaszcza, że jest to dosyć nowa funkcja w JavaScript.
Pamiętajmy – toSorted to sort, który nie modyfikuje tablicy oryginalnej, tylko zwraca posortowaną kopię.
Jak się do tego zabrać?
Array.prototype.toSorted = function() {
let sortedArray = this.slice();
sortedArray.sort();
return sortedArray;
};
Na dobry początek powinno wystarczyć. Tworzymy kopię tablicy, wywołujemy na niej sort, zwracamy posortowaną kopię.
Brakuje możliwości przekazania funkcji jako callback.
Array.prototype.toSorted = function(func=null) {
let sortedArray = this.slice();
if(func)
sortedArray.sort(func);
else
sortedArray.sort();
return sortedArray;
};
Teraz mamy możliwość użycia sort z funkcją własną albo domyślnego. Przetestujmy:
let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5];
let sortedNumbers = numbers.toSorted((a, b) => a - b);
console.log(sortedNumbers);
//[1, 1, 2, 3, 4, 5, 5, 6, 9]
Jak widać – działa.
Zadanie 2 – własna metoda toSpliced
JavaScript posiada metodę splice, która modyfikuje oryginalną tablicę (już mniejsza z tym jak ona działa – jeżeli pamiętamy, to bardzo dobrze).
Posiada też nasz JS od pewnego czasu metodę toSpliced, która zwraca nową tablicę ze splice wykonanym na niej. Nie modyfikuje oryginalnej, tylko kopię, którą zwraca.
Napisanie własnego toSpliced jest łatwiejsze, niż się wydaje:
Array.prototype.toSplied = function(...args) {
let copy = this.slice();
return copy.splice(...args);
};
Tworzymy funkcję, która przyjmuje zmienną liczbę argumentów (splice może przyjąć 2 albo więcej niż 2 argumenty).
Tworzymy kopię i zwracamy kopię, na której wykonujemy splice. Argumenty w args, zapakowane do zmiennej, rozpakowujemy.
Przykład:
let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5];
let splicedPart = numbers.toSplied(2, 5);
console.log(splicedPart);
//[4, 1, 5]
console.log(numbers);
//[3, 1, 4, 1, 5, 9, 2, 6, 5]
Wykonaliśmy splice na kopii. Kopii, w której zaczynając od indeksu 2 usunęliśmy 5 elementów. Oryginalna tablica pozostaje bez zmian.
Myślę, że na tym zadaniu zakończymy. Można powiedzieć, że razem ze znajomością innych metod tablicowych (map, filter, reduce, forEach) i tym co poznaliśmy oraz powtórzyliśmy, wiemy już o tablicach w JS naprawdę dużo.
Teraz co najwyżej możemy sobie wiedzę utrwalić.
Do następnego razu.