9

Pole a cykly

Užitečnost počítačů spočívá především v tom, že dokážou velmi rychle provádět opakovaně nějakou činnost, tedy provádět takzvaný cyklus.

Do této chvíle jsme uměli do nějaké proměnné uložit pouze jednu hodnotu, například řetězec, DOM element nebo funkci. Počítače ale často potřebují pracovat v mnohem větším objemem dat, než je několik čísel nebo řetězců. V této lekci si ukážeme jak ukládat větší množství hodnot do jedné proměnné a jak potom můžeme s takovými hodnotami zpracovávat pomocí cyklů.

Pole

Pole Array nám umožňují do jedné proměnné uložit více hodnot. Pokud znáte například jazyk Python, pole v JavaScriptu jsou totéž co seznamy v Pythonu.

Pole tvoříme pomocí hranatých závorek. Takto například do jedné proměnné uložíme známky ze všech písemek psaných za jedno pololetí.

> const marks = [3, 1, 2, 4]

Uvnitř polí je možné mít zcela libovolné hodnoty, tedy například řetězce, DOM elementy apod.

> const temperaturs = [13.5, 12.7, 11.2, 12.3, 15.1]
> const users = ['john', 'sue', 'peter', 'jane', 'soji']

Samotné pole je ovšem také hodnota. Není tedy problém mít například pole polí.

> const expenses = [['john', 250], ['sue', 170], ['peter', 337]]

Pozor na to, že podobně jako existuje prázný řetězec '', existuje také prázdné pole []. Je to zcela běžná hodnota, která se často velmi hodí.

Indexy

Hodnoty uvnitř polí sídlí na takzvaných indexech. Programátoři však mají takový zvláštní fetiš, že všechno počítačí počínaje nulou, nikoliv jedničkou. Má to svoje důvody, které však zatím nebudeme rozvádět. Raději si rovnou ukážeme, jak přistupovat k hodnotám na jednotlivých indexech.

> const marks = [3, 1, 2, 4]
undefined
> marks[0]
3
> marks[3]
4

Pomocí indexů také můžeme hodnoty uvnitř pole měnit. Dejme tomu, že si poslední nehezkou čtyřku opravíme na dvojku.

> marks[3] = 2
2
> marks
[ 3, 1, 2, 2 ]

U polí, která obsahují další pole, se k jednotlivým prvkům dostaneme pomocí vícenásobného indexování. Takto například zjistíme, kolik utratil Petr v našem polí výdajů.

> const expenses = [['john', 250], ['sue', 170], ['peter', 337]]
undefined
> expenses[2][1]
337

Vlastnosti a metody

Pole také mají zajímavé vlastnosti a metody. Vlastnost length už známe z řetězců.

> marks.length
4

Pomocí metody push můžeme přidat novou hodnotu na konec pole.

> marks.push(1)
5
> marks
[ 3, 1, 2, 2, 1]

Naopak pomocí metody pop poslení prvek pole smažeme.

> marks.pop()
1
> marks
[ 3, 1, 2, 2 ]

Pokud chceme odebrat prvek ze začátku pole, použijeme metodu shift.

> marks.shift()
3
> marks
[ 1, 2, 2 ]

Chceme-li přidat prvek na začátek pole, je to vlastně opačná operace k shift. Metoda pro přidání prvku na začátek pole ma tedy trochu zláštní jméno unshift.

> marks.unshift(1)
4
> marks
[ 1, 1, 2, 2 ]

Pomocí metody includes můžeme zjistit, jestli se unvitř pole nechází zadaný prvek.

> marks
[ 1, 1, 2, 2 ]
> marks.includes(1)
true
> marks.includes(3)
false

Metoda indexOf nám přímo řekne první index, na kterém se zadaný prvek v poli nachází. Pokud prvek v poli není, obdržíme -1.

> marks
[ 1, 1, 2, 2 ]
> marks.indexOf(2)
2
> marks.indexOf(3)
-1

Řetězce versus pole

Řetězce jsme v tomto kurzu potkali na samém začátku. Nyní si však můžeme ukázat, že řetězce mají s poli hodně společného. V některých situacích se totiž chovají jako pole znaků. Už víme, že pole i řetězce mají vlastno length. Můžete však také přistupovat k jednotlivých znakům skrze indexy.

> const name = 'Ernest'
undefined
> name.length
6
> name[1]
'r'
> name[5]
't'

Na řetězcích také najdeme metody includes a indexOf. Tentokrát však můžeme vyhledávat nejen písmenka, ale celé podřetězce.

> const text = 'Kobyla má malý bok'
undefined
> text.includes('b')
true
> text.includes('malý')
true
> text.indexOf('malý')
10
> text.indexOf('velký')
-1

Použití polí

Pole v programování potkáme na každém kroku. Můžeme je například použít pro reprezentaci dat. Takto například pomocí reprezentujeme tabulku výdajů z našeho úplně prvního příkladu se spolubydlením.

const expenses = [
  ['Petr', 'Prací prášek', 240],
  ['Ondra', 'Savo', 80],
  ['Pavla', 'Toaleťák', 65],
  ['Zuzka', 'Mýdlo', 50],
  ['Pavla', 'Závěs do koupelny', 350],
  ['Libor', 'Pivka na kolaudačku', 124],
  ['Petr', 'Pytle na odpadky', 75],
  ['Míša', 'Utěrky na nádobí', 130],
  ['Ondra', 'Toaleťák', 120],
  ['Míša', 'Pečící papír', 30],
  ['Zuzka', 'Savo', 80],
  ['Petr', 'Tapeta na záchod', 315],
  ['Ondra', 'Toaleťák', 64],
];

Pomocí pole polí můžeme také reprezentovat herní plány v různých počítačovách hrách. Takto například mohou vypadat rozehrané piškvorky 3x3.

const tictactoe = [
  ['o', ' ', ' '],
  [' ', 'x', ' '],
  [' ', 'o', 'x'],
];

Vidíme, že na tahu je zrovna křížek. Můžeme tak snadno provést nějaký chytrý tah.

> tictactoe[0][2] = 'x'
> tictactoe
[
  ['o', ' ', 'x'],
  [' ', 'x', ' '],
  [' ', 'o', 'x'],
];

Cvičení: Práce s poli

1

Pole v divadle

pohodička

Celé toto cvičení prováďejte v JavaScript konzoli.

  1. Vytvořte nějaké pole celých čísel, například počty diváků na několika po sobě jdoucích divadelních představeních. Použije metodu push a přidejte do pole počet diváků na novém představení.
  2. Vytvořte nějaké pole desetinných čísel, například zaplněnost divadla v několika po sobě jdoucích představeních.
  3. Vytvořte nějaké pole řetězců, například seznam názvů několika divadelních představení, která divadlo hraje. Uložte toto pole do proměnné plays. Uložte do nějaké proměnné druhou položku tohoto pole. Pomocí metody shift odstraňte z pole první představení, které už divadlo nehraje.
  4. Do proměnné reviews uložte pole hodnocení, které obdržela divadelní hra Plyšáci na útěku v různých recenzních časopisech. Hodnocení je vždy dvouprvkové pole obsahující název recenzního časopisu jako řetězec a samotné hodnocení jako číslo mezi 1 až 10. Pomocí metody unshift přidejte na začátek nové hodnocení z časopisu Divadelní oběžník.
2

Šachovnice

to dáš

Založte JavaScriptový program a vytvořte v něm pole, které bude reprezentavat rozehranou šachovou partii jako na obrázku.

Šachovnice

  1. Šachovnici reprezentujte jako pole polí, kde každý řádek je reprezentován jako jedno pole. Políčka reprerezentujte jako čísla dle následujícího klíče.
    figura bilá černá
    prázdné pole0
    pěšec1-1
    věž2-2
    kůň3-3
    střelec4-4
    dáma5-5
    král6-6
  2. Otevřete tento program v konzoli a napište příkaz, který přesune bílého koně z pozice f3 na pozici e5.

Cykly

Pole a cykly jsou jakýmsi svatým grálem začátečníckého programování. Otvírají cestu k mnohem zajímavějším a komplexnějším programům a aplikacím. Cykly slouží k tomu, abychom mohli nějaký kus kódu provádět opakovaně. Síla počítačů spočívá právě v tom, že dokážou extrémně rychle provádět opakovanou činnost a tím nám ušetřit mnoho práce. V JavaScriptu existuje vícero typů cyklů a v této lekci se zatím představíme pouze ty hlavní a základní.

Cyklus WHILE

Na nejpřímočařejší použití cyklu narazíme ve chvíli, kdy chceme provést nějaký kus kódu vícekrát po sobě. Pro jednoduchost zečněme tím, že budeme chtít do konzole vypsat čísla od 1 do 10. Toho dosáhneme následujícím kódem.

let num = 1;
while (num <= 10) {
  console.log(num);
  num += 1;
}

Na začátku si vytváříme proměnnou num, která bude obsahovat naše čísla. Poté následuje cyklus, který vypadá velmi podobně jako podmínka if. Podobně jaku u podmínek, i zde se do kulatých závorkách píše výraz, který vrací pravdivostní hodnotu. Pokud tento výraz vrátí true, vykoná se zadaný blok kódu. Jakmile však blok kódu skončí, výraz v kulatých závorkách se vyhodnotí znovu. Pokud opět vrátí true, blok kódu se vykoná znova. Takto se cyklus opakuje tak dlouho, dokud výraz v závorkách nevrátí false.

Protože v bloku kódu vždy přičteme k proměnné num jedničku, cyklus se nám zopakuje dohromady desetkrát.

Cyklus nemusíme použít jen k počítání. Můžeme například nechat uživatele zadávat heslo tak, dlouho, dokud nezadá to správné.

let pass = prompt('Enter your password:');
while (pass !== 'fatboyslim') {
  pass = prompt('Wrong password. Try again:');
}

Tento program je na uživatele zbytečně zlý. Lepší by nejspíš bylo, kdybychom počet opakování omezili například na maximálně pět.

let attempt = 1;
let pass = prompt('Enter your password:');
while (pass !== 'fatboyslim' && attempt < 6) {
  pass = prompt('Wrong password. Try again:');
  attempt += 1;
}

Pozor na to, že stačí malá nepozornost a vyrobíme cyklus, který se opakuje do nekonečna.

let num = 1;
while (num <= 10) {
  console.log(num);
  num + 1;
}

V praxi se nám občas nekonečný cyklus hodí, ale většinou vzníká jako chyba v programu. Jistě se vám již mnohokrát stalo, že počítač takzvaně zamrznul. Většinou je to právě proto, že nějaký program se omylem dostal do nekonečné smyčky.

Procházení pole

Velmi často budeme používat cyklus k tomu, abychom prošli nejaké pole porvek po prvku a s každým prvkem něco udělali. V takovém případě si vyrobíme cyklus, který prochází jednotlivé indexy pole a tyto indexy pak používáme k přístupu k jednotlivým prvkům. Zkusme například vypsat na obrazovku naše pole známek.

const marks = [2, 4, 1, 3, 4, 3];

let i = 0;
while (i < marks.length) {
  console.log(marks[i]);
  i += 1;
}

Aby cyklus správně fungoval, musíme si dát dobrý pozor jednak na to, abychom s indexm i začínali od nuly a bychom nepřekročili délku pole.

Všimněte si, že už po několikáté vidíme cyklus, kde používáme nějakou proměnnou, kterou postupně navyšujeme dokud nedojdeme k nějakému limitu. Protože tato proměnná vlastně řídí, jak dlouho se celý cyklus bude opakovat, říkáme jí řídící proměnná control variable .

Cyklus FOR

Cyklus s řídící proměnnou je v programování tak častý, že si pro něj programátoři vytvořili speciální syntaxi, která se jmenuje FOR cyklus. V cyklech FOR jsou všechny příkazy s řídící proměnnou na jednom místě a kód je pak lépe čitelný.

Ukažme si ještě jednou cyklus WHILE pro výpis známek do konzole.

const marks = [2, 4, 1, 3, 4, 3];

let i = 0;
while (i < marks.length) {
  console.log(marks[i]);
  i += 1;
}

Nyní pohlédněte na tutéž funkčnost zapsanou pomocí cyklu FOR.

const marks = [2, 4, 1, 3, 4, 3];

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

Vidíte že, že nyní všechny příkazy pro práci s řídící proměnnou pěkně na začátku cyklu a nemusíme se rozlížet po celém kódu, abychom je našli.

Složitejší cykly

K zajímavějším programům se dostaneme ve chvíli, kdy uvnitř cyklů začneme pracovat s proměnnými mimo cyklus. Takto například spočítáme průměrnou známku.

const marks = [2, 4, 1, 3, 4, 3];

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

const averageMark = sum / marks.length;

Tímto způsobem se můžeme propracovat až k velmi složitým algoritmům, cyklům uvnitř cyklů apod. V této lekci se však budeme zatím držet při zemi.

Cvičení: Cykly

3

Počítání pomocí WHILE

to dáš
  1. Napište cyklus WHILE, který do konzole vypíše všecha čísla od jedné až do sta.
  2. Napište cyklus WHILE, který do konzole vypíše všecha sudá čísla od jedné až do sta.
  3. Napište cyklus WHILE, který do konzole vypíše všecha sudá čísla pozpátku od sta až k nule.
4

Počítání pomocí FOR

to dáš

Přepište všechy WHILE cykly z předchozího cvičení na FOR cykly.

5

Uživatelé

to dáš

Založte JavaScriptový program s následujícím seznamem uživatelů.

const users = ['paja', 'kaja', 'vlasta', 'peta', 'alex', 'misa'];
  1. Pomocí cyklu FOR vypište všechna tato jména do konzole.
  2. Do konzole vypište všechna jména jako emailové adresy z domény gmail.com. Uživatel ‘paja’ tak bude ‘paja@gmail.com’.
  3. Vypište do konzole emaily pouze těch uživatelů, jejichž jméno má nejvýše 4 znaky.
6

Pohyby na účtu

zapni hlavu

Založe JavaScriptový program s následujícím s záznamem vkladů a výběrů na nějakém bankovním účtu.

const amounts = [2500, -550, 1000, -1200, -3000, 1270, 2300];
  1. Napište cyklus FOR, který spočítá výsledný zůstatek na účtu za předpokladu, že na účtu byla na začátku nula.
  2. Napište cyklus WHILE, který zjistí číslo pohybu, ve kterém se účet dostal poprvé do mínusu. Vypište jak číslo pohybu, tak výši záporného zůstatku.

Povinné čtení na doma

Díky tomu, že už rozumíme polím, si můžeme představit funkci document.querySelectorAll. Tato funguje podobně jako už známá funkce document.querySelector. Vrátí nám však všechny elementy, které najde pomocí zadaného selektoru. Výsledek obdržíme jako pole DOM elementů. To pak můžeme zpracovat pomocí nějakého cyklu.

V lekci o událostech jsme si slíbili, že díky cyklům dokážeme zjednodušít kód naší aplikace se smajlíky. Připomeňme si HTML kód našich tlačítek.

<button id="btn-smiley1" class="btn-smiley">😀</button>
<button id="btn-smiley2" class="btn-smiley">😍</button>
<button id="btn-smiley3" class="btn-smiley">😜</button>
<button id="btn-smiley4" class="btn-smiley">😢</button>
<button id="btn-smiley5" class="btn-smiley">😱</button>

Náš výsledný učesaný JavaScriptový kód vypadal takto.

'use strict';

const selectSmiley = (event) => {
  event.target.classList.add('btn-smiley--selected');
};

document.querySelector('#btn1').addEventListener('click', selectSmiley);
document.querySelector('#btn2').addEventListener('click', selectSmiley);
document.querySelector('#btn3').addEventListener('click', selectSmiley);
document.querySelector('#btn4').addEventListener('click', selectSmiley);
document.querySelector('#btn5').addEventListener('click', selectSmiley);

Všimněte si, že kód pro nasazení posluchače na událost click jsme museli opakovat pětkrát. Díky document.querySelectorAll můžeme nyní posluchače nasadit na všechna tlačítka jedním cyklem.

'use strict';

const selectSmiley = (event) => {
  event.target.classList.add('btn-smiley--selected');
};

const buttons = document.querySelectorAll('.btn-smiley');
for (let i = 0; i < buttons.length; i += 1) {
  buttons[i].addEventListener('click', selectSmiley);
}

Doporučené úložky na doma

7

Počítání hodin

zapni hlavu

Napište cyklus WHILE, který do konzole postupně vypíše všechny časové údaje v rámci jednoho dne od 0:00 až po 23:59.

Ukázka výstupu:

0:00
0:01
0:02
...
23:58
23:59
8

Čekání na šestku

to dáš

Založte JavaScriptový program a splňte následující úkoly.

  1. Napište funkci roll, která simuluje hod kostkou. Vrátí tedy náhodné číslo od 1 do 6 tak, že všechna čísla mají stejnou pravděpodobnost.
  2. Váš program nechť hází kostkou tak dlouho, až poprvé padne šestka. Vypište na výstup na kolikátý pokus šestka padla.
  3. Spusťte váš program desetkrát za sebou a zaznamenejte výsledky. Sdílejte vaše výsledky s ostatními abychom nasbírali co nejvíce dat.
9

Malé algoritmy

to dáš

Založte si nový JavaScriptový program a na jeho začátek vložte následující seznam čísel. Budeme jej používat ve všech následujících úlohách.

const numbers = [
  -24, -11, 27, 29, -4, -28, -21, -14, 3, -8, 24, 19, -25, -2, -1, 11, 32, -31, 5
];
  1. Vypište na výstup všechna čísla.
  2. Vypište na výstup druhé mocniny všech čísel.
  3. Vypište na výstup pouze záporná čísla.
  4. Vypište na výstup absolutní hodnotu všech čísel.
  5. Vypište na výstup pouze ta čísla, jejíchž absolutní hodnota je dělitelná třemi.
  6. Vypište na výstup pouze sudá čísla.
  7. Vypište na výstup jak daleko je každé číslo v seznamu od čísla 5.
  8. Vypište na výstup druhé mocnicny vzdáleností všech čísel od čísla 5.
  9. Spočítejte, kolik je v seznamu záporných čísel.
  10. Spočítejte součet všech čísel v poli.
  11. Spočítejte průměr všech čísel v poli.
  12. Spočítejte součet všech pozitivních čísel v poli.

Nepovinné úložky na doma

10

Registrace

zapni hlavu

Stáhněte se připravenou stránku s registračním formulářem pro nového uživatele. Doplňte do stránky JavaScriptový kód tak, aby byly splněny následující požadavky.

  1. Pokud uživatel zadá uživatelské jméno, které je již obsaženo v poli users, vypište do prvku s třídou reg-form__error chybovou hlášku ve smyslu, že zadané uživatelské jméno je již zabráno.
  2. Zkontrolujte, že heslo zadané do prvního políčka je dostatečně bezpečené. Heslo považujeme za bezpečné, pokud má alespoň 10 znaků nebo obsahuje alespoň jeden ze znaků pomlčka -, podtržítko _ nebo dvojtečka :.
11

Těžší algoritmy

zapni hlavu

Založte si nový JavaScriptový program a na jeho začátek vložte následující seznam čísel. Budeme jej používat ve všech následujících úlohách.

const numbers = [
  -24, -11, 27, 29, -4, -28, -21, -14, 3, -8, 24, 19, -25, -2, -1, 11, 32, -31, 5
];
  1. Vypište na výstup všechna čísla, která jsou větší než jejich předchůdce.
  2. Vypište na výstup všechny vrcholy v daném poli. Vrchol je číslo, které je větší než jeho předchůdce i jeho následovník.
  3. Nejprve spočítejte průměr čísel v poli a výsledek si ulože do proměnné mean. Poté spočítejte součet druhých mocnin vzdáleností všech čísel od průměru.
  4. Spočítejte směrodatnou odchylku čísel v poli. To provedete tak, že nejprve spočítáte takzvaný rozptyl, což je průměr druhých mocnin vzdáleností všech čísel od průměru. Rozptyl pak odmocníte pomocí funkce Math.sqrt.
  5. Najděte s seznamu největší prvek.
  6. Najděte s seznamu největší prvek, který je menší než číslo 16.
12

Algoritmy pro fajnšmekry

zavařovačka

Založte si nový JavaScriptový program a na jeho začátek vložte následující seznam čísel. Budeme jej používat ve všech následujících úlohách.

const numbers = [
  -24, -11, 27, 29, -4, -28, -21, -14, 3, -8, 24, 19, -25, -2, -1, 11, 32, -31, 5
];
  1. Vypište na výstup délku nejdelší rostoucí sekvence čísel, které v poli následují přímo po sobě.
  2. Vypište na výstup délku nejdelší rostoucí nebo klesající sekvence čísel, které v seznamu následují přímo po sobě.
  3. Najděte druhý největší prvek v seznamu.
  4. Najděte nejnižší vrchol a nejvyšší údolí. Údolí je číslo, které je menší než jeho předchůdce i následovník.