JavaScript Restart – Hurá na pole

Velkou bolestí JavaScriptu bývalo poměrně málo nativních funkcí pro práci s poli v JavaScriptu, a klávesy “F”, “O” a “R” jsou na klaviaturách našich živitelů značně osahané. Pole je, vzhledem k chudému počtu (2 v ES4) typů použitelných pro vytváření datových struktur, druhým nejužívanějším.

Seriál: JavaScript Restart (4 díly)

  1. JavaScript Restart – QuerySelector 11.2.2015
  2. JavaScript Restart – Hurá na pole 23.2.2015
  3. JavaScript Restart – Neidentifikovatelný létající objekt 9.3.2015
  4. JavaScript Restart – Restartováno 27.3.2015

Array.forEach

První (a vlastně nejméně zajímavou funkcí) je forEach, a to zejména proto, že nám umožní pouze nahradit “for” cyklus. Takže místo obligátního:

for (var i = 0; i < items.length; i++) {
    item = items[0];
    //kód
}

můžeme použít toto:

items.forEach(function(item, i) {
    //kód
});

Zatím žádný zázrak a navíc malý kámen úrazu, a tím je kontext callback funkce, čili je si třeba ohlídat, aby reference this znamenala to, co chceme my (protože jinak je kontext window). To se dá zařídit druhým argumentem.

items.forEach(function(item, i) {
     //kód
}, context);

Array.forEach je ekvivalentem $().each(), ovšem proč standardizátoři Javascriptu nepoužili kratší each, je pro mě záhadou. Možná protože je tu přeci jen jeden rozdíl, a to v počtu parametrů callback funkce. Obsahuje totiž:

  1. aktuální položku pole
  2. index
  3. referenci na procházené pole

jQuery v $().each() předává callbacku pouze první dva parametry. Druhým rozdílem je, že vrací vždy undefined na rozdíl od $().each(), která vždy vrací pole.

Malý příklad použití v praxi – dejme tomu, že chceme pro každý button zaregistrovat stejný handler. Pak kód v jQuery bude vypadat takto:

$('button').click(handler);

a čistém JavaScriptu takto:

document.querySelectorAll('button').forEach(function(element) {
    element.addEventListener('click', handler);
});

Uznávám, že proti jQuery je toto poněkud ukecané.

Nemohu se na tomto místě nezmínit o sadistických sklonech některých vývojářů, kteří vymýšlejí vtipné mozkolamy a variace na for cyklus, jako třeba:

for (var i = 0; i < items.length; ++i) {
    //kód
}

Čtení takového kódu bolí, a chudák dědic jejich kódu ještě večer usíná s otázkou: “Proboha proč?”

Array.map

Šikovnou funkcí je map, která je právě hojně využívaná v jQuery, zřejmě proto, že se s ní dá nadělat spousta parády.

Array.map na rozdíl od Array.forEach už cosi vrací, a to pole s hodnotami, které vrátíme v callback funkci.

Následující příklad neudělá nic lepšího než kopii pole:

var newItems = items.map(function(item) {
    return item;
}, context);

Pojďme se tedy podívat na něco užitečnějšího. Máme pole id elementů a chceme na základě tohoto pole dostat přímo pole elementů.

var ids= [
    '#name',
    '#lastname',
    '#age'
];

var els = ids.map(function(id) {
    return document.querySelectAll(id);
});

Array.filter

Array.filter je funkce na první pohled podobná Array.map s tím rozdílem, že vrací položky pole, pro které vyhověla podmínka v callbacku.

Následující kód vrátí nová pole, která nebudou obsahovat undefined hodnoty z pole items:

var newItems = items.filter(function(item) {
    return item !== undefined;
}, context);

Následující příklad slouží k odstranění duplicitních hodnot z pole (nechal jsem se inspirovat tímto příkladem):

var uniqueArray = duplicates.filter(function(elem, pos, duplicatesArray) {
    return duplicatesArray.indexOf(elem) == pos;
});

Šikovné, že?
Další užitečné využití funkce Array.filter je při hledání v poli objektů. Chceme-li například dostat seznam všech objektů, které jsou pouze pro čtení, provedeme to takhle:

var objectList = [
    {readonly: false},
    {readonly: true},
    {readonly: true}
];

var readonlyItems = objectList.filter(function(item) {
    return item.readonly;
});

Array.reduce

Array.reduce je prazvláštní funkce, jejíž dokumentaci jsem musel přečíst několikrát, než jsem pochopil, co vlastně dělá. Taktéž je zajímavá tím, že momentálně není implementována v jQuery (na rozdíl od filter a map).

Její callback funkce dostává tyto parametry:

  1. výsledek vrácený při předchozím spuštění callback funkce
  2. aktuální položku pole
  3. index
  4. procházené pole

a právě v první položce je kámen úrazu, jelikož na první pohled to vypadá, že vrací předchozí prvek pole, ale není tomu tak.
Vypadá to nějak takhle:

var newItems = items.reduce(function(previousItem, currentItem) {
    //kód
});

Na co tohle použít? Kupříkladu jsem již několikrát hledal elegantní způsob, jak sečíst všechny prvky v poli. S Array.reduce je to hračka:

var sum = itemToSum.reduce(function(previous, current) {
  return previous + current;
});

Zde je na místě otázka, jaká hodnota je v prvním parametru previous při prvním průchodu. Je to hodnota první položky, kterou však lze přednastavit jako parametr funkce Array.reduce.

var sum = [1,2,3].reduce(function(previous, current) {
  return previous + current;
}, 10);

Výsledek bude tudíž číslo 16.

Array.every

Další z fukcí pole, která vrací jednu hodnotu, je Array.every. Ta nad každou položkou pole provede test, a budou-li všechny vracet true, pak iArray.every vrací true.

Budeme-li chtít vědět, zda jsou všechny objekty (z výše uvedeného příkladu) pouze ke čtení, můžeme použít toto:

var objectList = [
    {readonly: true},
    {readonly: false},
    {readonly: true}
];

var readonly = objectList.every(function(item) {
    return item.readonly;
});

Při použítí této funkce je třeba myslet na to, že pokud jednou dostane false, už se dále nepokračuje, čili v tomto příkladě se třetí prvek pole netestuje.

Array.some

A když už máme funkci pro test na všechny položky pole, tak ještě přidáme test pro alespoň jednu. Je to tak, Array.some vrací true, pokud alespoň jeden test vrátí true.

var readonly = [
    {readonly: false},
    {readonly: true},
    {readonly: true}
].every(function(item) {
    return item.readonly;
});

A opět je třeba nezapomenout, že cyklus skončí ve chvíli, kdy bude rozhodnuto, tudíž na třetí položku opět nedojde.

Všechny výše zmíněné funkce jsou implementovány v Internet Exploreru 9+, tudíž, pokud nás IE8 netrápí, můžeme je zvesela používat.

Perlička na závěr

K povídání o “polních” funkcích v JavaScriptu přihodím malý trik. Často se stane, že je třeba ověřit funkce validátoru, konkrétně validaci délky, jenže kde honem vzít řetězec např. o délce 255 znaků.

Řešení vypadá takto:

Array(256).join('a');

Proč 256, proč ne 255? Pojďme se podívat na to, co taková konstrukce vlastně dělá.

Array(256);

Vytvoří pole o 256 prvcích undefined. Následně metoda join mezi tyto prvky vloží 255 krát “a”. A máme hotovo.

Další možné řešení je pomocí funkce Array.fill:

Array(255).fill('a').join('');

anebo ještě lépe pomocí String.repeat, což je přesně to, co hledáme:

'a'.repeat(255)

Jenže tyto funkce jsou součástí ES6 a momentálně si je můžete vyzkoušet pouze ve Firefoxu nebo z polyfillem.

Pracuje jako vývojář webových aplikací ve společnosti TopMonks, s.r.o. a specializuje se na JavaScript, AngularJS a EmberJS, hlavně na vývoj uživatelských rozhraní. Je členem kapely Rezatý Rakety. Když nehraje ani neprogramuje, inhaluje výpary při lepení plastikových modelů.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 28

Přehled komentářů

Paladin Mozkolam
pavel Re: Mozkolam
Miroslav Juhos Re: Mozkolam
Stanislav Nechutný Re: Mozkolam
Paladin Re: Mozkolam
Dave Re: Mozkolam
Peter Re: Mozkolam
alesroubicek Re: Mozkolam
Peter Re: Mozkolam
Dash Typografické uvozovky/apostrofy v kódu?
Miroslav Juhos Re: Typografické uvozovky/apostrofy v kódu?
Martin Zkousel jste vubec ten kod?
Miroslav Juhos Re: Zkousel jste vubec ten kod?
Miroslav Juhos Re: Zkousel jste vubec ten kod?
DavidGrudl Re: Zkousel jste vubec ten kod?
karel for
Jinaq Re: for
ivoszz Re: for
peci1 reduce
tomas.pavlacky attachEvent
Miroslav Juhos Re: attachEvent
martin _
Honza Re: _
alesroubicek Re: _
martin Re: _
mkoubik
mkoubik Re:
Snehuliak Javascript verzia
Zdroj: https://www.zdrojak.cz/?p=14380