Lekce 2 - Filtrování a mapování polí ve Swift

Swift Kolekce Filtrování a mapování polí ve Swift

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Úvod do kolekcí a genericita ve Swift, jsme si vysvětlili genericitu ve Swift. Dostáváme se k další důležité části programování. Jak již doufám víte z lekce o poli ve Swiftu, často se hodí uložit několik prvků stejného typu a poté s nimi dále pracovat. Právě k tomuto účelu kolekce existují. Swift má nabídku kolekcí velmi přímočarou a nabízí tři primární:

  • Array - pole, které již známe
  • Dictionary - slovník
  • Set - množina

Každá z kolekcí se hodí k něčemu jinému, nejčastěji budete používat Array.

Array

Swift implementuje Array jako moderní pole a hojně se v tomto jazyce využívá. Jen pro zopakování nemusíme přesně určovat jeho velikost, nijak se o ni starat, a máme k dispozici metody na pohodlné přidávání položek, jejich odebírání a také několik dalších. Ty základní již známe, takže je zde přeskočíme. O pokročilých metodách polí bude právě dnešní lekce.

Založme si novou konzolovou aplikaci s názvem např. Kolekce a v main.swift si připravme jednoduché pole čísel.

var cisla = [2, 5, 9, 12, 34, 17, 28, 18]

filter()

Velmi často potřebujeme pole "přefiltrovat" a získat pouze prvky, které vyhovují nějaké podmínce. Tuto podmínku zapíšeme do speciálního bloku nazvaného v angličtině closure. Nyní nám stačí vědět, že se jedná o blok kódu podobný funkci, který můžeme předat nějaké metodě jako parametr. V podstatě předáme funkci funkci, možná to zní zmateně, ale když se nad tím zamyslíte, dává to smysl. Např. zavoláme metodu filter() a té řekneme, aby nám pole profiltrovala jen na hodnoty, které odpovídají naší closure (funkci). Právě metodě filter() nyní opravdu předáme closure specifikující jak profiltrovat naše pole čísel. Pokud začneme psát cisla.filter a zbytek kódu si necháme "dopsat" od Xcode, tak dostaneme něco takového:

cisla.filter(isIncluded: (Int) throws -> Bool)

Closure se zapisuje takto pomocí šipky, již bychom tedy uměli definovat metodu, která ji bere jako argument. To my ale nechceme a proto znovu potvrdíme klávesou Enter. Xcode nám vygeneruje prázdnou closure. Výsledek vypadá takto:

cisla.filter { (Int) -> Bool in
code
}

Výraz (Int) -> Bool označuje, že z čísla (typ Int) potřebujeme zjistit zda má v poli zůstat nebo ne (typ Bool). Int v závorce a také "code" v bloku máme zvýrazněné. Do závorky, kde je nyní Int, zapíšeme název proměnné pro jednotlivé prvky v poli, abychom s nimi mohli dále pracovat. Jelikož v našem poli máme čísla, přejmenujeme proměnnou na cislo:

cisla.filter { (cislo) -> Bool in
}

Metoda filter() pro každé číslo v našem poli provede náš zatím prázdný closure a podle vrácené hodnoty Bool určí, jestli bude číslo v novém vyfiltrovaném poli. Dejme tomu, že chceme ponechat pouze čísla větší než 10, takže do bloku napíšeme:

return cislo > 10

Výsledný kód tedy vypadá následovně:

cisla.filter { (cislo) -> Bool in
    return cislo > 10
}

Když výsledek vypíšeme, tak vidíme, že všechna čísla opravdu splňují tuto podmínku.

print(cisla)
Zkrácení zápisu

Možná si říkáte, že pro filtrování potřebujeme nějak zbytečně moc kódu, když vlastně pouze porovnáváme číslo s hodnotou 10. Swift naštěstí nabízí o poznání zkrácený zápis, ale musíme ho napsat celý sami.

cisla.filter { $0 > 10 }

To je mnohem lepší, že? $0 zastupuje proměnnou, která se v našem případě jmenovala cislo a return není potřeba. Je totiž jasné, že se tento výraz vrací a použije se pro vyhodnocení návratové hodnoty. Všimněte si, že z kódu zmizely kulaté závorky. Této syntaxi se říká tzv. trailing closure. Pokud by měla metoda i nějaké standardní parametry, uvedli bychom je do kulaté závorky, tu ukončili, a následně předali closure. Mohlo by to vypadat např. takto:

instance.metoda("nějakýParametr") { $0 > 10 }

Když nyní umíme pole filtrovat, ukážeme si i podobnou a neméně užitečnou metodu, map().

map()

Metoda slouží k tzv. transformaci dat na jiný datový typ nebo do jiné struktury. V closure nám potom stačí definovat, jak chceme existující data na nová přeměnit. Například bychom chtěli všechna čísla vynásobit dvěma. Nejdříve si opět ukážeme, jak vypadá vygenerovaný kód od Xcode. Z metody filter() je vám určitě povědomý.

cisla.map { (Int) -> T in
}

T zde nahrazuje konkrétní datový typ a jedná se o genericitu, kterou jsme si vysvětlili minule. Nyní nám stačí T nahradit za datový typ, který chceme vrátit jako výsledek. Jelikož pouze násobíme, tak výstupní typ zůstane Int. Kompletní volání metody map() pro vynásobení prvků pole dvěma by vypadalo následovně:

cisla.map { (cislo) -> Int in
        return cislo * 2
}

Samozřejmě jej můžeme opět zkrátit na trailing colusure:

cisla.map { $0 * 2 }

Možná vás napadá, že toto vše byste mohli udělat v nějakém cyklu i bez znalosti closures. Bylo by to ale pracnější, bylo by potřeba založit nové pole a jednotlivé prvky do něj vkládat. Navíc díky metodám filter() a map() je při čtení kódu hned jasné, o co se snažíme.

Násobení čísel je samozřejmě velmi základní případ. map() bychom spíše využili např. kdybychom měli pole objektů Student a chtěli získat pole jejich emailových adres.

V příští lekci, Slovníky (Dictionary) ve Swift, se budeme věnovat slovníkům, které Swift implementuje jako třídu Dictionary.


 

 

Článek pro vás napsal Filip Němeček
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
Autor se příležitostně věnuje vývoji iOS aplikací či těch webových za pomocí frameworku Django. Aktuální projekt: hrejzdarma.cz Twitter: @nemecek_f (neprogramátorské tweety)
Miniatura
Všechny články v sekci
Kolekce ve Swift
Miniatura
Následující článek
Slovníky (Dictionary) ve Swift
Aktivity (2)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!