geschreven door Reinder de Vries op 9 juli 2020 In App Development, Swift

toewijzen, verkleinen en filteren in Swift

in Swift gebruikt u map(), reduce() en filter() om collecties zoals arrays en woordenboeken te luseren, zonder een for-loop te gebruiken.

de functies map, reduce en filter komen uit het domein van functioneel programmeren (FP). Ze worden hogere-orde functies genoemd, omdat ze functies als input nemen. Je past een functie toe op een array, bijvoorbeeld, om zijn gegevens te transformeren.

Swift ‘ s kaart -, verminder-en filterfuncties kunnen een uitdaging zijn om je hoofd rond te wikkelen. Vooral als je altijd for in lussen hebt gecodeerd om iteratieproblemen op te lossen. In deze gids leert u hoe u de functies map(_:), reduce(_:_:) en filter(_:) in Swift kunt gebruiken.

aan de slag!

  1. Inleiding tot mappen, verkleinen en filteren
  2. Snelstart: Hogere-Orderfuncties in Swift
  3. gebruikmakend van de functie Map
  4. gebruikmakend van de functie Reduce
  5. gebruikmakend van de filterfunctie
  6. samenvoegen van Map, Reduce en Filter
  7. verder lezen

Inleiding tot Map, Reduce en Filter

wanneer u iOS-apps bouwt, gebruikt u meestal procedureel of objectgeoriënteerd programmeren. Functioneel programmeren is anders: het gaat alleen over functies. Geen variabelen, geen toestand, Geen for-loops-just functies.De Swift – programmeertaal leent zich uitstekend voor het combineren van functioneel programmeren met niet-functionele benaderingen, zoals OOP. U hoeft niet strikt functionele code te schrijven, en het aannemen van concepten uit functioneel programmeren kan u helpen om beter te coderen.

de functies map(_:), reduce(_:_:) en filter(_:) worden functies met een hogere orde genoemd, omdat ze een functie als input nemen en als output retourneren. Technisch gezien geeft Swift de resultaten van een operatie (d.w.z. een getransformeerde array) bij gebruik van functies van hogere orde, terwijl een pure functionele taal een verzameling functies zal teruggeven. In Swift zijn de ingangen voor deze functies sluitingen.

zo werken ze:

  • de functie map() past een functie toe op elk item in een verzameling. Denk aan het “in kaart brengen” of het transformeren van een set van waarden in een andere set van waarden.
  • de functie reduce() verandert een verzameling in één waarde. Zie het als het combineren van vele waarden in één, zoals het gemiddelde van een reeks getallen.
  • de functie filter() geeft gewoon waarden terug die een if-statement passeerden, en alleen als die voorwaarde resulteerde in true.

in het geval u denkt: “kijk, ik heb geen functionele programmering of gegevensverwerking nodig, omdat mijn apps dat niet doen!”stop dan hier niet. In recente app projecten heb ik gebruikt Map, verminderen en filteren op meerdere gelegenheden:

  • Filteren op de kosten/inkomsten waarden met filter(_:), om te voldoen aan een drempel, voorafgaand aan het tonen van de waarden in een grafiek lijn
  • Gemiddelde duizenden film ratings in één waarde reduce(_:_:)
  • Mapping een paar handelingen op een string met hashtags, om te zetten in een genormaliseerde collectie, met map(_:)

Je zou kunnen hebben opgelost al deze problemen met een for-lus, maar u zult zien dat het gebruik van map(), reduce() en filter() resulteert in meer beknopte, leesbare en performante code.

Snelstart: Hogere-orde functies in Swift

we zullen ons richten op map(), reduce() en filter() in deze tutorial. Voordat we verder gaan, hier is een snel overzicht van de meest voorkomende hogere-orde functies in Swift:

  • map(_:) loops over elk item in een reeks, past een functie toe op elk element, en retourneert het getransformeerde resultaat
  • reduce(_:_:) lussen over elk item in een reeks, combineert ze tot één waarde, en retourneert het gecombineerde resultaat
  • filter(_:) lussen over elk item in een reeks en retourneert een resulterende reeks die alleen items bevat die voldoen aan een bepaalde filterfunctie
  • flatMap(_:) doet hetzelfde als map(_:), behalve dat het de resulterende reeks afvlakt, d.w.z. geneste arrays zijn niet genest of “afgevlakt””
  • compactMap(_:) doet hetzelfde als map(_:), behalve dat het nil waarden verwijdert uit de resulterende reeks voordat u het retourneert

u kunt deze functies gebruiken op arrays, woordenboeken, sets, ranges, sequenties en elk ander Swift-type dat u kunt herhalen. Als u meer wilt weten over compactMap(_:) en flatMap(_:), bekijk dan deze tutorial.

verschillende types in Swift, zoals Array en Dictionary, hebben functies die sluitingen als invoer accepteren. Een snelle keuze:

  • contains(where:) lussen over een collectie, is een predicaat (sluiting) voor ieder item geeft een true als een item voldoet aan het predicaat, anders false
  • first(where:) lussen over een collectie, is een predicaat (sluiting) voor elk item, en geeft de zaak, indien het voldoet aan het predicaat
  • firstIndex(where:) doet hetzelfde als first(where:), behalve dat het resultaat van de index in plaats van de waarde

Je kunt meer te weten komen over deze functies in deze tutorial. Hoe where wordt gebruikt in Swift is ook interessant, daar kun je meer over leren in deze tutorial.

u hebt de functies “map” en “reduce” gezien in Swift geschreven als map(_:) en reduce(_:_:) gedurende deze tutorial. De underscores en dubbele punten in deze functies maken deel uit van de handtekening van de functie, die een speciaal formaat is om functieparameters aan te geven. De functie map(_:) heeft bijvoorbeeld één parameter zonder naam, terwijl de functie reduce(_:_:) er twee heeft. U kunt daar meer over leren in deze tutorial: functies in Swift uitgelegd.

word aangenomen als iOS-ontwikkelaar

leer iOS-apps te bouwen met Swift 5

meld je aan voor mijn iOS-ontwikkelcursus en leer hoe je je carrière als professionele iOS-ontwikkelaar kunt beginnen.

met behulp van de kaartfunctie

de functie map(_:) loopt over elk item in een verzameling en past een bewerking toe op elk element in de verzameling. Het retourneert een verzameling van resulterende items, waarop de bewerking is toegepast.

laten we een voorbeeld bekijken. We hebben een reeks temperaturen in Celsius die je wilt omzetten in Fahrenheit.

u kunt een for-loop gebruiken, zoals dit:

let celsius = var fahrenheit: = for value in celsius { fahrenheit += }print(fahrenheit)// Output: 

de code werkt prima, maar het is te uitgebreid. Je hebt een veranderlijke “helper” variabele fahrenheit nodig om de berekende conversies op te slaan terwijl je er doorheen werkt, en je hebt 3 regels code nodig voor de conversie zelf.

zo kunnen we hetzelfde doen met de functie map(_:) :

laat Celsius =
laat fahrenheit = celsius.kaart { $0 * (9/5) + 32 }
afdrukken (fahrenheit))
Verberg waarschuwingen

u kunt dit zelfs allemaal op één regel doen:

.map {  * (9/5) + 32 }

Wat gebeurt hier?

  1. een constante celsius wordt gedefinieerd, een array van dubbele waarden, en geïnitialiseerd met een paar willekeurige Celsius waarden.
  2. de functie map(_:) wordt aangeroepen op de celsius array. De functie heeft één argument, een sluiting, die van Celsius naar Fahrenheit converteert.
  3. tenslotte wordt het resultaat afgedrukt: de geconverteerde array, van Celsius naar Fahrenheit.

de functie map(_:) transformeert een array in een andere, door een functie toe te passen op elk item in de array. De closure * (9/5) + 32 neemt de invoerwaarde in Celsius en geeft een waarde in Fahrenheit terug. De resulterende array van map(_:) is opgebouwd uit deze geconverteerde waarden.

laten we de afsluiting nader bekijken. Als u eerder met sluitingen hebt gewerkt, herkent u de syntaxis van de sluiting met korte hand. Het is een kortere manier om een sluiting te coderen, door veel van zijn syntaxis weg te laten.

hier is een minder beknopt alternatief:

let celsius = let fahrenheit = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})print(fahrenheit)

de werkelijke map(_:) functieaanroep, en de afsluiting ervan, is dit:

··· = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})

Wat is daar aan de hand? De functie map(_:) wordt aangeroepen op de array celsius. Er is één argument voor nodig: een afsluiting van type (Double) -> Double.

het eerste deel van de afsluiting, beginnend met {, geeft aan dat deze afsluiting één parameter value van type Double heeft, en de afsluiting geeft een waarde van type Doubleterug. Het sluitlichaam, beginnend met return, geeft gewoon het resultaat van de Celsius-Fahrenheit-berekening terug.

als u de syntaxis van de sluiting met korte hand vergelijkt met de uitgebreide code hierboven, zult u zien dat:

  • de functiehaakjes ( en ) worden weggelaten, omdat u deze kunt weglaten wanneer de laatste parameter van een functieaanroep een afsluiting is.
  • het () -> in deel kan worden weggelaten, omdat Swift kan concluderen dat u een Double parameter gebruikt als invoer, en er wordt verwacht dat u een Doubleretourneert. Nu u de value variabele hebt weggelaten, kunt u de short-hand gebruiken.
  • het return statement kan ook weggelaten worden, omdat verwacht wordt dat deze afsluiting toch het resultaat van een expressie zal teruggeven.

hoewel de bovenstaande codesteekproef Double typen gebruikt, bent u niet beperkt tot deze typen. Het resulterende type van een map() functie kan een ander type hebben dan wat je erin zet, en je kunt map() ook gebruiken op een Dictionary.

laten we verder gaan naar reduce(_:_:)!

gebruikmakend van de functie Reduce

de functie reduce(_:_:) loopt over elk item in een verzameling en reduceert deze tot één waarde. Zie het als het combineren van meerdere waarden in één.

de functie verkleinen is misschien wel de moeilijkst te begrijpen map, verkleinen, filteren. Hoe kun je van een verzameling van waarden naar één waarde gaan?

enkele voorbeelden:

  • het creëren van een som van meerdere waarden, d.w.z. 3 + 4 + 5 = 12
  • het samenvoegen van een verzameling tekenreeksen, d.w.z. = "Zaphod, Trillian, Ford"
  • Het gemiddelde van een reeks waarden, d.w.z. (7 + 3 + 10) / 3 = 7/3 + 3/3 + 10/3 = 6.667

in de gegevensverwerking, kunt u zich veel scenario ‘ s voorstellen wanneer eenvoudige bewerkingen als deze van pas komen. Net als voorheen kunt u elk van deze problemen oplossen met een for-loop, maar reduce(_:_:) is gewoon korter en sneller.

dit is hoe:

let values =
let sum = values.verminderen (0, +)
afdrukken (Som)
waarschuwingen verbergen

de functie reduce(_:_:) heeft twee argumenten, een beginwaarde en een afsluiting. In de bovenstaande code leveren we de + operator, dat is ook een functie met twee parameters.

u kunt natuurlijk ook uw eigen afsluiting verzorgen:

let waarden =
let gemiddelde = waarden.verminderen (0.0) { $0 + $1 } / dubbel (waarden.aantal)
afdrukken (gemiddeld)
Verberg waarschuwingen

in het voorbeeld hierboven berekent u het gemiddelde van drie getallen. De typen waarden in de code zijn alle Double. We tellen eerst alle getallen op en delen het door het aantal getallen.

de functie reduce(_:_:) is op twee manieren verschillend:

  1. de functie reduce(_:_:) heeft twee niet nader genoemde parameters; de beginwaarde en de sluiting
  2. de sluiting die wordt verstrekt aan reduce(_:_:) heeft ook twee parameters; het huidige resultaat van de reductie, en de nieuwe waarde die op het punt staat gereduceerd te worden

hier, Bekijk dit:

let values =
let sum = values.verminderen (0) {
afdrukken(“\($0) + \($1) = \($0 + $1)”)
return $0 + $1
}
afdrukken (Som)
Verberg waarschuwingen

in het bovenstaande voorbeeld kunt u duidelijk zien en , de 2 parameters van de sluitingen. Als je de code uitvoert, is dit de uitvoer die je krijgt:

0 + 7 = 77 + 3 = 1010 + 10 = 2020

zie je hoe we beginnen met 0 en dan 7toevoegen? In de volgende stap nemen we 7 – de huidige reductiewaarde – en voegen we 3, de “volgende” waarde in de reductie toe.

hier is een andere manier om ernaar te kijken:

0 + 7(0 + 7) + 3((0 + 7) + 3) + 10

dit maakt ook duidelijk waarom je een initiële waarde voor reduce(_:_:) nodig hebt, omdat dat de eerste eerste parameter van de afsluiting is.

reductie kan moeilijk te begrijpen zijn. Het is belangrijk om te begrijpen dat je iteratief een operatie toepast, zoals +, op een verzameling waarden totdat je nog maar één waarde hebt. Je verlaagt letterlijk het aantal waarden.

laten we verder gaan naar filter(_:)!

met behulp van de filterfunctie

de functie filter loopt over elk item in een verzameling en geeft een verzameling terug die alleen items bevat die aan een include-voorwaarde voldoen.

het is als het toepassen van een if-statement op een verzameling, en alleen de waarden behouden die aan de voorwaarde voldoen.

hier, Bekijk dit:

let waarden =
let even = waarden.filter {$0 .isMultiple (van: 2) }
print (even)
Verberg waarschuwingen

in het bovenstaande voorbeeld filtert u getallen van values die even zijn. De functie isMultiple(of:) geeft true terug wanneer gedeeld kan worden door 2, en false anders.

in tegenstelling tot map(_:) en reduce(_:_:) moet de afsluiting filter(_:) een boolean teruggeven, dus ofwel true of false. Wanneer de afsluiting true retourneert, wordt de waarde behouden, en wanneer false wordt geretourneerd, wordt de waarde weggelaten. Zo filtert filter(_:) de invoer array.

hier is een iets duidelijker voorbeeld, met de sluiting uitgebreid:

let values = let even = values.filter({ (value:Int) -> Bool in return value.isMultiple(of: 2)})print(even) // Output: 

In het voorbeeld geeft de afsluiting een Booleaanse waarde terug, aangegeven met -> Bool. Het heeft één parameter, het item in de collectie, en geeft het resultaat van isMultiple(of:)terug. Gaaf!

Map, verkleinen en filteren combineren

kunt u de functies map(), reduce() en filter() combineren? Natuurlijk kun je dat!

stel dat we een klas studenten hebben. Je weet het jaar dat elke student werd geboren. Je wilt de gecombineerde leeftijd berekenen van alle studenten geboren in of na 2000.

zo doe je dat:

let now = 2020
let years =
let sum = years.filter({ $0 >= 2000 }).kaart ({ now – $0 }).verminderen (0, +)
afdrukken (Som)
waarschuwingen verbergen

het bovenstaande codevoorbeeld maakt gebruik van chaining. Het gebruikt het resultaat van een functie als input voor een andere, het combineren van map-verminderen-filter. De functie map() wordt aangeroepen op de resultatenreeks van de functie filter(), en de functie reduce() wordt aangeroepen op het resultaat van de functie map(). Geweldig!

de code zelf is eenvoudig:

  1. Maak een constante now en years, en wijs er een aantal jaren aan toe.
  2. filteren de jaren die lager zijn dan 2000, d.w.z. houd de leeftijdsgroepen waarvan >= 2000 true
  3. elk jaar omzetten in een leeftijd, door het jaar af te trekken van now
  4. Tel alle leeftijden op, door met +

laten we een interessanter voorbeeld nemen. Bekijk de volgende code, overgenomen uit de tutorial over FizzBuzz:

laat FizzBuzz: (Int)- > String = { i in
switch (I % 3 == 0, i % 5 == 0)
{
case (true, false):
return “Fizz”
case (false, true):
return “Buzz”
case (true, true):
return “FizzBuzz”
default:
return ” \(i)”
}
}
laat resultaat = Array (2…100).kaart (fizzbuzz).verminderen(“1”, { $0 + “, ” + $1 })
afdrukken (resultaat)
Verberg waarschuwingen

zie wat er aan de hand is? We transformeren een array met getallen van 2 tot 100 in “Fizz”,” Buzz “of” FizzBuzz ” met map(_:), gebaseerd op de spelregels. Tot slot reduceren we die array van strings tot één grote string met reduce(_:_:), waarbij elke waarde wordt gecombineerd. Gaaf!

verder lezen

wat als je dit alles moest coderen met for in loops? Je zou veel meer code gebruiken. En dat is de macht van map-reduce-filter: het is beknopter, vaak makkelijker te lezen, en — geef het toe-behoorlijk verdomd cool!

wilt u meer weten? Bekijk deze bronnen:

  • hoe “where” te gebruiken in Swift
  • FlatMap en CompactMap uitgelegd in Swift
  • the Ultimate Guide to Closures in Swift
  • Hoe: zoek een Item in een Array In Swift
  • speel met Code: Binair zoeken in Swift
  • aan de slag met Xcode Playgrounds

word aangenomen als iOS-ontwikkelaar

leer iOS-apps te bouwen met Swift 5

meld je aan voor mijn iOS-ontwikkelcursus en leer hoe je je carrière als professionele iOS-ontwikkelaar kunt beginnen.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.