Přeskočit na hlavní obsah

Javascript - map / reduce / filter

V době, kdy jsem se poprvé setkal s javascriptem, nic nenaznačovalo tomu, že by se jednou z tohoto jazyka stalo to, co prožíváme nyní.
Psát o tom, že je to jazyk, který se vrací jako bumerang a v současné době zažívá své znovuzrození, je stejně zbytečné, jako to, že je to jazyk, který má asi nejsvětlejší budoucnost ze všech.

Díky bohu, že jsme se postupně dostali přes různé slepé uličky či uzavřeli jQuery, které splnilo svou historickou povinnost a odešlo do věčných lovišť. Již dávno jsou pryč časy, kdy většina vývojářů k javascriptu přistupovalo způsobem: google -> stackoverflow -> copy & paste.

Díky nové specifikaci (ES5 a ES6), jsme schopni k tomuto jazyku již přistupovat mnohem přímočařeji.

Dnešní ukázka se bude týkat práce s polem. V ukázce je použit typescript.

1. Představte si, že máte uživatele, který má následující vlastnosti:

interface User {
    readonly id: number;
    readonly firstName: string;
    readonly lastName: string;
    readonly roles: string[];
    readonly countOfClicks: number;
}

2. Nyní si vytvoříme pole uživatelů

const users: User[] = [
    {id: 1, firstName: 'Ales', lastName: 'Dostal', roles: ['admin', 'operator', 'manager'], countOfClicks: 32},
    {id: 2, firstName: 'Petr', lastName: 'Vomacka', roles: ['operator', 'manager'], countOfClicks: 43},
    {id: 3, firstName: 'Martin', lastName: 'Novak', roles: ['manager'], countOfClicks: 0},
    {id: 4, firstName: 'Petr', lastName: 'Novotny', roles: ['operator'], countOfClicks: 13},
    {id: 5, firstName: 'Jan', lastName: 'Novak', roles: ['manager', 'superuser'], countOfClicks: 31},
    {id: 6, firstName: 'Petr', lastName: 'Sulek', roles: ['manager', 'superuser'], countOfClicks: 18},
];

Úkol 1: Celkový počet kliků


Prvním zadáním je celkový počet kliků, všech uživatelů.
Pokud bychom nepoužívali ES5 a arrow functions, tak by zápis vypadal následovně:

let countAll = 0;
for (let i = 0; i < users.length; i++) {
    countAll += users[i].countOfClicks;
}
console.log('countAll: ', countAll);

V případě využití ES5 a arrow functions, lze dané zadání přepsat následovně:

const countAll = users.reduce((result, user) => (result + user.countOfClicks), 0);
console.log('countAll: ', countAll);

Výsledek:
countAll: 137

Docela rozdíl, že? Pojdmě se podívat na další možnosti, které již budou využívat pouze ES5 a arrow functions. Nechceme přeci psát zastaralým stylem :)

Úkol 2: Průměrný počet kliků


Dalším úkolem je, znát půmerný počet všech kliků, přes všechny uživatele. Pokud bychom nevycházeli s předchozího výsledku, který bychom jen vydělili celkovým počtem uživatelů, je možné i tento úkol zapsat pomocí funkce reduce:

const average = users.reduce((result, user, index, array) => {
    result += user.countOfClicks;
    if (index === array.length - 1) {
        return result / array.length;
    }
    return result;
}, 0);
console.log('average: ', average);

Výsledek:
average: 22.833333333333332

Úkol 3: Najdi všechny uživatele s jménem Petr


Výsledkem tohoto úkolu by mělo být pole uživatelů, kteří splňují podmínku, že jméno uživatele je Petr.

const petrUsers = users.filter((f) => f.firstName === 'Petr');
console.log('petrUsers: ', petrUsers);

Výsledek:
petrUsers:  [ { id: 2,
    firstName: 'Petr',
    lastName: 'Vomacka',
    roles: [ 'operator', 'manager' ],
    countOfClicks: 43 },
  { id: 4,
    firstName: 'Petr',
    lastName: 'Novotny',
    roles: [ 'operator' ],
    countOfClicks: 13 },
  { id: 6,
    firstName: 'Petr',
    lastName: 'Sulek',
    roles: [ 'manager', 'superuser' ],
    countOfClicks: 18 } ]

Úkol 4: Pole ID uživatelů


Vysledkem úkolu je pole ID uživatelů

const ids = users.map((user) => (user.id));
console.log('ids: ', ids);

Výsledek:
ids: [ 1, 2, 3, 4, 5, 6 ]

Úkol 5: Unikátní pole rolí


Výsledkem bude pole rolí, kde se žádná z rolí nebude opakovat:

const roles = users.reduce((result, user) => {
    result.push(...user.roles.filter((f) => result.indexOf(f) === -1));
    return result;
}, []);
console.log('roles: ', roles);

Výsledek:
roles: ['admin', 'operator', 'manager', 'superuser']

Úkol 6: Pole jmen a počet výskytů


Výsledkem bude pole jmen a počet výskytů v poli uživatelů.


const uniqueFirstNameCount = users.reduce((result, user) => {
    result[user.firstName] = (result[user.firstName] || 0) + 1;
    return result;
}, []);
console.log('uniqueFirstNameCount: ', uniqueFirstNameCount);

Výsledek:
uniqueFirstNameCount: [Ales: 1, Petr: 3, Martin: 1, Jan: 1]

Závěr


Znalost využívání funkcí jako je map, reduce, filter, find či sort je jedna z klíčových věcí, kterou by programátor měl znát.
Díky tomu, že javascript je jazyk, který lze dnes využívat i na serveru, tak díky znalosti funkcí pro pole je možné tuto znalost aplikovat, například pro mongodb.
Pokud by někdo našel optimálnější způsob zápisu, daných úkolů, nechť se o ně podělí v komentáři.

Komentáře

  1. Přál bych si, aby tyhle tři metody používalo více lidí. Takže díky za sepsání!

    V návaznosti na výzvu v poslední větě přikládám, jak bych tyto úkoly vyřešil já. Neříkám, že je to tak lepší, jen pro porovnání ;)

    1) Celkový počet kliků

    ```
    const sum = (prev, next) => prev + next

    const countAll = users
    .map(({ countOfClicks }) => countOfClicks)
    .reduce(sum)
    ```

    Výhoda je, že `sum` je obecná znovupoužitelná metoda.

    2) Průměrný počet kliků

    ```
    const average = users
    .map(({ countOfClicks }) => countOfClicks)
    .reduce((prev, n, index) => (index * prev + n) / (index + 1))
    ```

    Zde v reduce vždy předávám průměr předchozích čísel. Příjde mi to univerzálnější a dalo by se to použít např. s observables.

    3) + 4) Udělal bych (samozřejmě) stejně, jen bych hodnotu získal z argumentu pomocí destructuringu `({ id }) => id`

    5) Unikátní pole rolí

    ```
    const flatten = (prev, next) => prev.concat(next)

    const roles = new Set(users
    .map(({ roles }) => roles)
    .reduce(flatten)
    )

    console.log([...roles])
    ```

    Unikátní hodnoty, skoro volají po Set :D

    6) Pole jmen a počet výskytů

    ```
    const updateMap = (updater) => (map, key) =>
    map.set(key, updater(map.get(key)))

    const addOne = (n = 0) => n + 1

    const firstNameCounts = users
    .map(({ firstName }) => firstName)
    .reduce(updateMap(addOne), new Map())

    console.log(firstNameCounts)
    // -> Map(4) {"Ales" => 1, "Petr" => 3, "Martin" => 1, "Jan" => 1}
    ```

    Jak se píše na stránce o Map na MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) jedno z kritérií pro použití Map místo objektu je: „Are keys usually unknown until run time?“ Tohle je přesně ten případ.
    Ta konstrukce s `updateMap` se mi líbí, protože je jasné, co se kdy děje, a je to opět znovupoužitelné.





    OdpovědětVymazat
    Odpovědi
    1. Koukám, že se nazachovalo formátování kódu :(
      Pokud řádek začíná tečnou, byl odsazený. Stejně tak je odsazený druhý řádek funkce `updateMap `.

      Vymazat
    2. Ahoj.
      Skvěle přepsané.
      I když se ti to nezformátovalo, tak čitelné to je.

      Já jsem se držel víc při zemi, co se týče funkcionálního zápisu. Funkcionální přístup je tak mocný, že občas je to na úkor přehlednosti.
      Vždycky hledám střední cestu. Srozumitelnost vs efektivní zápis.

      Object destructuring jsem nepoužil záměrně, protože jsem na tuto oblast chtěl napsat článek. Ale souhlas, že se zde hodí :)

      Vymazat

Okomentovat

Populární příspěvky z tohoto blogu

Jak si v IT vydělat hodně peněz?

Na začátek by bylo dobré, abych objasnil samotný titulek, který může na někoho působit jako červený hadr. Článek nebude o obecných pravidlech, ale bude vyprávět můj vlastní příběh, na kterém vám zkusím ukázat, jak se dá docílit úspěchu, či alespoň správně nastartovat svojí vlastní kariéru v IT.

I když se z názvu článku dá dedukovat, že se vše bude točit kolem peněz, není tomu tak. Alespoň ze dvou třetin určitě ne. Ale to už předbíhám, pojďme to raději vzít hezky popořadě...

Kdybychom měli mluvit o roce 2017 jako o přelomové době, nejspíše to nebude pravda. I když pro někoho to může být rok plný úspěchů a štěstí v podobě narození zdravých dětí, svatby či první velké lásky, tak z pohledu lidstva se jedná o rok, který jen kopíruje předešlé a v oblasti technologií nás posouvá stejným tempem jako rok předtím.

Jsem naprosto přesvědčen o tom, že i když se současná doba tak nenazývá, tak prožíváme dobu, která jednou bude označena za revoluční, a to zejména díky vynálezu internetu, který je s…

Jak by se firmy neměly chovat k programátorům?

Každý, kdo pracuje v IT oboru, se jistě již setkal s různými „geniálními nápady“, od kterých si firma slibovala zlepšení produktivity či snížení nákladů. Ať už je to zavedení agilních principů, striktní kontrola práce či zavedení nové a skvělé metodiky, o které si „šéf“ přečetl včera na internetu. Jsou z toho skutečně tak nadšení i samotní vývojáři? A bude nový nápad fungovat?
K napsání tohoto článku mě navedly různé programátorské diskuze, kde si lidé stěžovali na firmu, kde pracují. Příklady, které zde uvedu, jsou z reálné praxe. Ať už jsem je zažil jako řadový programátor, či jako šéf týmu.
I když je poptávka po programátorech tak vysoká, že Vás headhunteři nahánějí i ve chvílích, kdy o to opravdu nestojíte, tak i přes to je mnoho lidí, kteří se bojí opustit svoje současné zaměstnání.
Čeho se nejčastěji bojíme? Je to samozřejmě nejistota, kterou si často omlouváme větami jako: „Tady mám své pohodlí, co když to jinde mít nebudu?“ nebo „I když mě to v práci štve a nebaví, tak mě ale…

Jak jsem technologicky postavil startup

Tento příběh pojednává o technologiích, nástrojích a vůbec o všem, co jsem potřeboval k tomu, abych byl schopen, postavit startup na zelené louce.

Každý správný příběh začíná stejně: "Jednou jsem...."

Kapitola první: Nápad
Jednou jsem se setkal s člověkem, který měl nápad na produkt, který se v průmyslu zatím nevyskytuje. I přes prvotní skepsi, kdy jsem si říkal: "Tohle už přeci dávno v průmyslu existuje, ne?", jsem došel ke zjištění, že nikoli.

Tím jsem se dostal ke svému prvnímu poučení. Průmysl je technologicky dost zabržděný. Osobně se domnívám, že těch důvodů, proč tomu tak je, je několik. Za prvé je to fakt, že většina lidí, kteří se pohybují v tomto odvětví jsou často konzervativní a za správné považují pouze léty osvědčené věci. Druhým důvodem je to, že jakákoli změna znamená riziko. Ať už z pohledu finanční ztráty tak i z pohledu stability výroby. No a třetím a nejzásadnějším důvodem je to, že ač zde máme spousty technologických vymožeností, narážíme na to,…