Wszystko co musisz wiedzieć o metodzie map() w JavaScript

Czym jest metoda map()? Jak z niej korzystać?

Metoda map() jest jedną z częściej używanych metod tablicowych w JS, dlatego ważne jest, abyś dobrze ją opanował. Gdy wywołamy map() na obiekcie lub stringu, otrzymamy błąd: Uncaught TypeError: map is not a function.

METODA MAP() NIE MODYFIKUJE ORYGINALNEJ TABLICY, natomiast podczas wywołania tworzy nową tablicę i wstawia do niej elementy z tablicy oryginalnej, na których zostały wykonane transformacje z funkcji zwrotnej, podanej w parametrze metody. W najprostszej postaci wygląda to następująco:

[].map(callbackFn);

A po dodaniu wartości do tablicy oraz prostego działania funkcji zwrotnej:

const numbers = [1, 5, 6, 7];
const multiply = numbers.map( number => number * 2 ); 
console.log(multiply); //[2, 10, 12, 14]

W powyższym skorzystałem z funkcji strzałkowej dostępnej od ES6 – jeśli z jakiegoś powodu nie jest Ci jeszcze znana, wyjaśnię jej działanie w jednym z kolejnych artykułów.

Powyższa funkcja map() przechodzi kolejno po każdym elemencie tablicy numbers przekazując jego wartość jako argument funkcji znajdującej się po prawej stronie funkcji strzałkowej (w przedstawionym przypadku mnoży każdy z elementów tablicy numbers przez 2), a następnie zapisuje wynik do tablicy multiply. W wyniku działania powyższej funkcji, tablica numbers nadal będzie zawierać elementy [1, 5, 6, 7], natomiast tablica multiply = [2, 10, 12, 14].

Czy muszę używać funkcji strzałkowej przy korzystaniu z map()?

Nie, powyższy zapis możemy zmienić na:

const numbers = [1, 5, 6, 7];
const multiply = numbers.map( function(number) {
  return number * 2;
});

Jakby to było za mało, zarówno samo map() jak i funkcja używana jako callback mogą zawierać dodatkowe parametry:

const numbers = [1, 5, 6, 7];
const multiply = numbers.map( function(number, index, array) {
  console.log('index ' + index, 'tablica ' + array); 
  return number * 2;
}, this);
console.log(multiply);

Po otwarciu konsoli zobaczymy:

W powyższym przykładzie:

  • number – kolejna wartość z tablicy źródłowej, która po wykonaniu na niej funkcji callback zostanie zapisana do nowej tablicy.
  • index – kolejny numer pozycji w tablicy (począwszy od 0),
  • array – oryginalna tablica, na której pracujemy
  • this – drugi parametr funkcji map(), wskazuje kontekst użycia metody funkcji callback w map(). Przyjmuje wartość undefined, jeśli nie jest wskazany, nie musimy więc go dodawać gdy nie zmieniamy kontekstu.

W powyższym, tylko parametr number jest obowiązkowy. Oczywiście może mieć on dowolną nazwę.

Jak zmienić kontekst wywołania funkcji map()?

Spróbuję to wyjaśnić, modyfikując nieco powyższy kod:

const numbers = [1, 5, 6, 7];
const context = { multiplier: 2 };
const multiply = numbers.map(function(number) {
return number * this.multiplier;
}, context);
console.log(multiply); // [2, 10, 12, 14]

W tym przykładzie, this jest przekazywane jako drugi argument do funkcji map(), a obiekt context jest ustawiany jako kontekst wywołania dla funkcji zwrotnej. Dzięki temu możemy odwoływać się do właściwości multiplier z obiektu context wewnątrz funkcji map(), aby pomnożyć każdą liczbę z tablicy numbers.

Po co korzystać z map()? Przecież to samo mogę zrobić funkcją for().

const numbers = [1, 5, 6, 7];
const multiply = [];
for (let i = 0, max = numbers.length; i < max; i++) {
  multiply.push(numbers[i] * 2);
}
console.log(multiply); //[2, 10, 12, 14]

W wyniku działania użytej funkcji for() również tablica multiply również składa się z [2, 10, 12, 14], więc pytania jest zasadne, lecz jak to w programowaniu bywa – nie ma jednej słusznej drogi.

Na początek porównaj proszę kod z użyciem for() z kodem, który napisaliśmy na początku:

const numbers = [1, 5, 6, 7];
const multiply = numbers.map( number => number * 2);
console.log(multiply); //[2, 10, 12, 14];

Czy kod z użyciem funkcji strzałkowej nie wydaje Ci się on bardziej czytelny? Ilość kodu w przypadku korzystania z map() jest dużo mniejsza, więc czytelność jest dużo większa.

W przypadku, gdyby działo się coś więcej możemy dodatkowe operacje ująć w nawiasy klamrowe i na końcu zwrócić wartość obliczoną na ich podstawie:

const numbers = [1, 5, 6, 7];
const multiply = numbers.map( number => {
  const double = number * 2;
  return double;
});
console.log(multiply); //[2, 10, 12, 14];

Czytelność kodu to jednak nie wszystko. Ważniejsze jest to, co chcesz osiągnąć. W przypadku użycia map() dostaniesz nową tablicę, która ma dokładnie tyle elementów ile tablica oryginalna. Gdy użyjesz for(), masz pełną dowolność w kształtowaniu tablicy wynikowej – może ona zawierać mniej lub więcej elementów niż tablica źródłowa.

Np. gdy chcemy, aby iteracja przebiegła tylko na wartościach mniejszych niż 6, to dla map() moglibyśmy zrobić coś takiego:

const numbers = [1, 5, 6, 7];
const multiply = numbers.map( number => {
    if (number < 6) {
        return number * 2;
    }
});
console.log(multiply); //[2, 10, undefined, undefined]

Nie da to nam jednak tablicy 2-elementowej z wynikami [2, 10], ale czteroelementową (gdyż tyle elementów miała tablica początkowa), w której 2 ostatnie elementy będą miały wartość undefined. Motodą for() możemy natomiast dowolnie kształtować wynikową tablicę:

const numbers = [1, 5, 6, 7];
const multiply = [];
for (let i = 0, max = numbers.length; i < max; i++) {
    if (numbers[i] < 6) {
        multiply.push(numbers[i] * 2);
    }
}
console.log(multiply); //[2, 10]

Czy mogę używać map() do iterowania tablicy obiektów?

Oczywiście, z metody map() można korzystać do iterowania tablicy obiektów. Załóżmy, że mamy tablicę z markami i modelami samochodów:

const cars = [
  {brand: "Audi", model: "A4"},
  {brand: "VW", model: "Tiguan"},
  {brand: "Ford", model: "Mustang"}
]

Powyższą tablicę możemy teraz przeiterować za pomocą metody map():

const cars = [
  {brand: "Audi", model: "A4"},
  {brand: "VW", model: "Tiguan"},
  {brand: "Ford", model: "Mustang"}
];
const carsFullName = cars.map( cars => `${cars.brand} ${cars.model}`);
console.log(carsFullName);

W powyższym przykładzie, obok funkcji strzałkowej użyłem template string.

Tablica carsFullName zawiera teraz elementy [’Audi A4′, 'VW Tiguan’, 'Ford Mustang’];

Jak to jest z szybkością map()?

Map() jest nieco wolniejsze od for(), jednak różnice są tak niewielkie, że w większości projektów nie trzeba w ogóle o nich myśleć.

Funkcja map() w praktyce.

Czym byłaby teoria, gdyby nie dało się jej zastosować w praktyce. Weźmy na przykład takie zadanie (przykład zaczerpnięty z codewars.com, nieco zmieniony, rozwiązanie własne):

Podczas zakupów w Internecie, gdy podajemy nr swojej karty płatniczej, część numeru karty zostaje zamaskowana przez „#”. Widoczne są wtedy tylko ostatnie 4 cyfry z całego numeru. Za pomocą metody map() napiszmy funkcję – nazwijmy ją mask, która zamienia wszystkie znaki – oprócz ostatnich czterech – na „#”.

Przykłady działania:

mask('1234567890') == '******7890'
mask('1234567') == '***4567'
mask('Ala ma kota') == '*******kota'
mask('1') == '1'
mask('') == ''
Zanim zobaczysz rozwiązanie pomyśl i spróbuj rozwiązać to samodzielnie.
Ok, zaczynamy.
function mask(str) {
  const len = str.length - 4;

  return str.split('').map((val, index) => {
    return index < len ? '*' : val;
  }).join('');
}
console.log(mask('123456789'); //'*****6789

Wygląda strasznie? Wkrótce wytłumaczę za ten co tu się wydarzyło 🙂

 

Polecane

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *