Domů O mně Služby Portfolio Učím Blog Kontakt
← Zpět na učím

04.07 Reduce

Výuka

Proč Reduce?

reduce() je metoda, která redukuje celé pole na jednu hodnotu (číslo, string, objekt, nebo i nové pole). Je to nejuniverzálnější, ale také nejtěžší metoda pole.

Proč to potřebujeme?

  • Agregace - Součet, průměr, maximum, minimum
  • Transformace - Převeď pole na objekt nebo jiný formát
  • Složené operace - Kombinuj map, filter a další do jedné iterace
  • Accumulation - Nashromáždí výsledky postupně (jako sněhová koule)

Představ si to jako: Stavění sněhuláka. Začneš s malou sněhovou koulí (počáteční hodnota), postupně přidáváš sníh (prvky pole), a nakonec máš velkého sněhuláka (finální hodnota). Každý prvek pole přispěje k výsledku.

Jak to funguje?

reduce(fn, initialValue):
1. Zavolá funkci fn pro každý prvek pole
2. Funkce dostává akumulátor (accumulator) a aktuální prvek
3. Funkce vrací novou hodnotu akumulátoru
4. Akumulátor se "nese" přes všechny iterace
5. Vrací finální hodnotu akumulátoru (ne pole!)

Klíčové koncepty

  • reduce(reducer, initialValue) - redukuje pole na jednu hodnotu
  • Reducer - funkce: (accumulator, element, index, array) => newAccumulator
  • Accumulator - průběžný výsledek (nese se přes iterace)
  • Initial value - počáteční hodnota akumulátoru (důležitá!)
  • Univerzální - může nahradit map, filter, nebo obojí
  • Výsledek - může být číslo, string, objekt, nebo i nové pole

JavaScript

Příklad 1: Součet čísel (klasický příklad)

const numbers = [1, 2, 3, 4, 5];

const sum = numbers.reduce(function(accumulator, current) {
  return accumulator + current;
}, 0);

console.log(sum);
// → 15

// Krok po kroku:
// Iter 1: acc=0,  current=1  →  return 0+1=1   →  acc=1
// Iter 2: acc=1,  current=2  →  return 1+2=3   →  acc=3
// Iter 3: acc=3,  current=3  →  return 3+3=6   →  acc=6
// Iter 4: acc=6,  current=4  →  return 6+4=10  →  acc=10
// Iter 5: acc=10, current=5  →  return 10+5=15 →  acc=15
// Finální hodnota: 15

Co se stalo?

  • Počáteční hodnota: 0 (druhý argument reduce)
  • Reducer: (acc, curr) => acc + curr
  • V každé iteraci se aktuální prvek přičte k akumulátoru
  • Finální hodnota akumulátoru je součet všech prvků

Příklad 2: Arrow funkce (kratší syntax)

const numbers = [1, 2, 3, 4, 5];

// Součet
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum);
// → 15

// Součin
const product = numbers.reduce((acc, n) => acc * n, 1);
console.log(product);
// → 120 (1 * 2 * 3 * 4 * 5)

// Maximum
const max = numbers.reduce((acc, n) => n > acc ? n : acc, numbers[0]);
console.log(max);
// → 5

Co se stalo?

  • Součet: start od 0, přičítej prvky
  • Součin: start od 1 (ne 0!), násobuj prvky
  • Maximum: použij ternární operátor, vrať větší hodnotu

Příklad 3: Spojení textů (concatenation)

const words = ['Hello', 'world', 'from', 'reduce'];

const sentence = words.reduce((acc, word) => acc + ' ' + word, '');
console.log(sentence);
// → ' Hello world from reduce' (pozor na mezeru na začátku!)

// Lepší verze - bez mezery na začátku
const sentence2 = words.reduce((acc, word, index) => {
  return acc + (index > 0 ? ' ' : '') + word;
}, '');
console.log(sentence2);
// → 'Hello world from reduce'

// Nebo prostě použij join (jednodušší!)
const sentence3 = words.join(' ');
console.log(sentence3);
// → 'Hello world from reduce'

Co se stalo?

  • Můžeš spojovat stringy, ale pozor na počáteční hodnotu
  • Často je lepší použít join() pro spojování textů

Příklad 4: Transformace pole na objekt

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' }
];

// Vytvoř objekt indexovaný podle ID
const usersById = users.reduce((acc, user) => {
  acc[user.id] = user;
  return acc;
}, {});

console.log(usersById);
// → {
//   1: { id: 1, name: 'Alice' },
//   2: { id: 2, name: 'Bob' },
//   3: { id: 3, name: 'Charlie' }
// }

console.log(usersById[2]);
// → { id: 2, name: 'Bob' }

Co se stalo?

  • Počáteční hodnota: prázdný objekt {}
  • Postupně přidáváme prvky do objektu s klíčem podle ID
  • Výsledek: objekt pro rychlé vyhledávání podle ID

Příklad 5: Počítání výskytů (counting)

const fruits = ['jablko', 'banán', 'jablko', 'třešeň', 'banán', 'jablko'];

const count = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});

console.log(count);
// → { jablko: 3, banán: 2, třešeň: 1 }

Co se stalo?

  • Počáteční hodnota: prázdný objekt {}
  • Pro každý prvek: zvyš počet o 1 (nebo nastav na 1, pokud neexistuje)
  • acc[fruit] || 0 - pokud klíč neexistuje, použij 0
  • Výsledek: objekt s počty výskytů

Příklad 6: Kombinace map + filter (flatten)

const numbers = [1, 2, 3, 4, 5, 6];

// Udělej map + filter najednou pomocí reduce
const result = numbers.reduce((acc, n) => {
  if (n % 2 === 0) {  // Filtr - jen sudá čísla
    acc.push(n * 2);  // Map - vynásob dvěma
  }
  return acc;
}, []);

console.log(result);
// → [4, 8, 12]

// Porovnej s map + filter
const result2 = numbers
  .filter(n => n % 2 === 0)
  .map(n => n * 2);
console.log(result2);
// → [4, 8, 12]

Co se stalo?

  • reduce může nahradit map + filter
  • Počáteční hodnota: prázdné pole []
  • Jen jedno procházení pole (vs. dvě u map + filter)
  • Ale: map + filter jsou čitelnější!

TypeScript

TypeScript přidává typovou kontrolu akumulátoru a výsledku.

Stejné příklady s typy

// Součet s typy
const numbers: number[] = [1, 2, 3, 4, 5];
const sum: number = numbers.reduce((acc: number, n: number): number => acc + n, 0);

// TypeScript inferuje typy z počáteční hodnoty
const sum2 = numbers.reduce((acc, n) => acc + n, 0);  // acc i n jsou number

// Transformace na objekt
interface User {
  id: number;
  name: string;
}

const users: User[] = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
];

// Musíš explicitně typovat akumulátor, pokud je jiný typ než prvky pole
const usersById: Record<number, User> = users.reduce((acc, user) => {
  acc[user.id] = user;
  return acc;
}, {} as Record<number, User>);

// Nebo použij generický typ
const usersById2 = users.reduce<Record<number, User>>((acc, user) => {
  acc[user.id] = user;
  return acc;
}, {});

// Počítání s explicitním typem
const fruits: string[] = ['jablko', 'banán', 'jablko'];
const count: Record<string, number> = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {} as Record<string, number>);

// Chybová kontrola - špatný typ počáteční hodnoty
const invalid = numbers.reduce((acc, n) => acc + n, '');
// ❌ Error: Type 'string' is not assignable to type 'number'
// (TypeScript ví, že acc + n by mělo být number, ne string)

TypeScript přidává:

  • Inference typu akumulátoru - z počáteční hodnoty
  • Typovou kontrolu reduceru - acc, current a návratová hodnota musí souhlasit
  • Explicitní generický typ - reduce() pro složitější případy
  • Prevenci chyb - nemůžeš vrátit špatný typ z reduceru

Rozdíl JS vs TS

JavaScript:

  • reduce funguje bez typové kontroly
  • Můžeš omylem změnit typ akumulátoru
  • Počáteční hodnota může být cokoli
  • Flexibilnější, ale nebezpečnější

TypeScript:

  • Typ akumulátoru je inferován nebo explicitně zadaný
  • TypeScript zkontroluje, že reducer vrací správný typ
  • Lepší dokumentace a prevence chyb
  • Bezpečnější, čitelnější
// JavaScript - projde, ale způsobí problém
const numbers = [1, 2, 3];
const result = numbers.reduce((acc, n) => acc + String(n), 0);
// result je '0123' (string!), ne 6

// TypeScript - upozorní na změnu typu
const numbers: number[] = [1, 2, 3];
const result: number = numbers.reduce((acc, n) => acc + String(n), 0);
// ❌ Error: Type 'string' is not assignable to type 'number'

Tip

💡 VŽDY zadávej počáteční hodnotu:

const numbers = [1, 2, 3, 4, 5];

// ✅ S počáteční hodnotou - jasné a bezpečné
const sum = numbers.reduce((acc, n) => acc + n, 0);

// ⚠️ Bez počáteční hodnoty - použije první prvek
const sum2 = numbers.reduce((acc, n) => acc + n);  // acc=1, pak 1+2, 3+3, 6+4, 10+5

// ❌ Nebezpečné pro prázdné pole!
const empty = [];
const fail = empty.reduce((acc, n) => acc + n);  // TypeError: Reduce of empty array with no initial value

💡 Pro jednoduché případy použij specializované metody:

// ❌ Zbytečně složité
const sum = numbers.reduce((acc, n) => acc + n, 0);
const doubled = numbers.reduce((acc, n) => [...acc, n * 2], []);

// ✅ Čitelnější
const sum = numbers.reduce((acc, n) => acc + n, 0);  // OK pro součet
const doubled = numbers.map(n => n * 2);  // Lepší pro transformaci

💡 reduce vs map + filter - čitelnost vs výkon:

// ✅ Čitelnější - použij map + filter
const result = numbers
  .filter(n => n % 2 === 0)
  .map(n => n * 2);

// ⚠️ Rychlejší (jedna iterace), ale méně čitelné
const result = numbers.reduce((acc, n) => {
  if (n % 2 === 0) acc.push(n * 2);
  return acc;
}, []);

// Pro většinu případů preferuj čitelnost

Kvíz

Co vypíše tento kód?

const arr = [1, 2, 3];
const result = arr.reduce((acc, n) => acc - n);
console.log(result);

- Chybný výpočet

- Není to součet

- BEZ počáteční hodnoty reduce použije první prvek jako acc. Tedy: acc = 1 (první prvek), pak 1 - 2 = -1, pak -1 - 3 = -4

- Není to chyba (ale je to nebezpečné pro prázdná pole!)

Důležité: Bez počáteční hodnoty reduce použije první prvek jako akumulátor a začne od druhého prvku. Toto může způsobit nečekané výsledky! Vždy zadávej počáteční hodnotu.

Důležité: Bez počáteční hodnoty reduce použije první prvek jako akumulátor a začne od druhého prvku. Toto může způsobit nečekané výsledky! Vždy zadávej počáteční hodnotu.

🎯 Závěrečný projekt

Po dokončení všech 8 dílů vytvoříte jednoduchou Todо aplikaci v čistém JavaScriptu. Naučíte se, jak aplikovat vše, co jste se naučili, na reálný projekt.

Zobrazit podrobnosti projektu →

Připraveni začít?

Zaregistrujte se a získejte přístup ke všem dílům tohoto seriálu

Kontaktujte mě

Informace o seriálu

Obtížnost

Délka

Cca 480 minut

Počet videí

8 videí + projekty

Certifikát

Po dokončení obdržíte certifikát


Lekce v této sekci


Struktura lekcí (souborový strom)

06. Typescript specifika
  • v přípravě
08. Moduly tridy
  • v přípravě
09. React zaklady
  • v přípravě
10. React hooks
  • v přípravě
12. Nextjs server
  • v přípravě
13. Databaze auth
  • v přípravě
14. Nextjs pokrocile
  • v přípravě