10 Swift One Liners To Impress Your Friends

Posted by Umberto Raimondi on January 6, 2016 中文版

Update 10/17:This post has been updated for Swift 4

Update 09/16:This post has been updated for Swift 3

A few years ago, at the peak of the Functional Renaissance™, a blog post that presented 10 Scala functional one liners became quite popular and was rapidly followed by a series of articles that implemented the same one liners in other languages like Haskell, Ruby, Groovy, Clojure, Python, C#, F#, CoffeeScript.

We’ll never know how many people were actually impressed by those one liners during social gatherings, but my guess is that at least for the uninitiated the more complex examples were a good incentive to learn more about functional programming.

Let’s see how Swift fares against the other languages, trying to solve the same 10 exercises using one liners, maybe obtaining something interesting in the process (see #6 and #10).

Get the playground for this article from GitHub or zipped.

#1 Multiply each element of an array by 2

Not much to see in this first example, easily solvable using map as we all know.


(1...1024).map{$0 * 2}

#2 Sum a list of numbers

This exercise is solved using reduce and the plus operator, leveraging the fact that the plus operator is a function, but the solution is obvious, we’ll see in a moment a few more creative uses of reduce.


(1...1024).reduce(0,+)

#3 Verify if Exists in a String

Let’s verify if a tweet contains at least one of a few selected keywords using filter:


let words = ["Swift","iOS","cocoa","OSX","tvOS"]
let tweet = "This is an example tweet larking about Swift"
let valid = !words.filter({tweet.contains($0)}).isEmpty

valid //true

Update: @oisdk suggests a few better alternatives:


words.contains(where:tweet.contains)

Way more concise, and this one:


tweet.characters.split(separator:" ")
                .lazy
                .map(String.init)
                .contains(where:Set(words).contains)

#4 Read in a File

Reading a file into an array of lines is not possible through an easy built-in like in other languages but we can create something short that doesn’t need a for using a combination of split and map:


let path = Bundle.main.path(forResource:"test", ofType: "txt")

let lines = try? String(contentsOfFile: path!).characters
                     .split{$0 == "\n"}
                     .map(String.init)

if let lines=lines {
    lines[0] // O! for a Muse of fire, that would ascend
    lines[1] // The brightest heaven of invention!
    lines[2] // A kingdom for a stage, princes to act
    lines[3] // And monarchs to behold the swelling scene.
}

That last step with map and the string constructor turns our arrays of characters into strings.

#5 Happy Birthday to You!

This will display the Happy Birthday song to console, a simple use of map with a range and the ternary operator.


let name = "uraimo"
(1...4).forEach{print("Happy Birthday " + (($0 == 3) ? "dear \(name)":"to You"))}

#6 Filter list of numbers

In this case we are asked to partition a sequence using a provided filtering function. Many languages have in addition to the usual map, flatMap, reduce, filter, etc… also a partitionBy function that does exactly that, Swift as you know doesn’t have something similar (the NSArray function that filters by NSPredicate is not what we want here).

Therefore, we could solve this extending Sequence with a partitionBy function that we’ll use to partition an integer array:


extension Sequence{
   typealias Element = Self.Iterator.Element 
    
   func partitionBy(fu: (Element)->Bool)->([Element],[Element]){
       var first: [Element] = [] 
       var second: [Element] = []
       for el in self {
          if fu(el) { 
             first.append(el) 
          }else{ 
             second.append(el) 
          } 
       } 
       return (first,second) 
   } 
}

let part = [82, 58, 76, 49, 88, 90].partitionBy{$0 < 60}
part // ([58, 49], [82, 76, 88, 90])

It’s not really a one liner and the approach is imperative. But could we use filter to improve it a little?


extension Sequence{ 
   func anotherPartitionBy(fu: (Self.Iterator.Element)->Bool)->
          ([Self.Iterator.Element],[Self.Iterator.Element]){ 
      return (self.filter(fu),self.filter({!fu($0)})) 
   } 
} 

let part2 = [82, 58, 76, 49, 88, 90].anotherPartitionBy{$0 < 60}
part2 // ([58, 49], [82, 76, 88, 90])

This is slightly better, but it traverses the sequence two times and trying to turn this into a one liner removing the enclosing function will get us something with too much duplicated stuff (the filtering function and the array that will be used in two places).

Can we build something that will transform the original sequence into a partition tuple using a single stream of data? Yes we can, using reduce.


var part3 = [82, 58, 76, 49, 88, 90].reduce( ([],[]), { 
   (a:([Int],[Int]),n:Int) -> ([Int],[Int]) in 
   (n<60) ? (a.0+[n],a.1) : (a.0,a.1+[n])  
}) 

part3 // ([58, 49], [82, 76, 88, 90])

What we are doing here is building the result tuple that contains the two partitions, an element at a time, testing each element of the original sequence using the filtering function and appending this element to the first or second partition array depending on the filtering result.

Finally a true one liner but notice that the fact that the partition arrays are being built via append will actually make it way slower than the two previous implementations.

#7 Fetch and Parse an XML web service

Some of languages above don’t rely on external libraries and have more than one option available by default to deal with XML (e.g. Scala that “natively” albeit awkwardly supports XML parsing into objects), but Foundation provides only the SAX parser NSXMLParser, and as you may have already guessed we are not going to use it.

There are a few alternative open source libraries we could use in this case, some of them written in C or Objective-C and others in pure Swift.

This time we are going to use the pure-Swift AEXML:


let xmlDoc = try? AEXMLDocument(xmlData: NSData(contentsOf: URL(string:"https://www.ibiblio.org/xml/examples/shakespeare/hen_v.xml")!)!) 

if let xmlDoc=xmlDoc {
    var prologue = xmlDoc.root.children[6]["PROLOGUE"]["SPEECH"]
    prologue.children[1].stringValue // Now all the youth of England are on fire,
    prologue.children[2].stringValue // And silken dalliance in the wardrobe lies:
    prologue.children[3].stringValue // Now thrive the armourers, and honour's thought
    prologue.children[4].stringValue // Reigns solely in the breast of every man:
    prologue.children[5].stringValue // They sell the pasture now to buy the horse,
}

#8 Find minimum (or maximum) in a List

We have various ways to find the minimum and maximum of a sequence, among those the min and max functions:


//Find the minimum of an array of Ints 
[10,-22,753,55,137,-1,-279,1034,77].sorted().first 
[10,-22,753,55,137,-1,-279,1034,77].reduce(Int.max, min) 
[10,-22,753,55,137,-1,-279,1034,77].min() 

//Find the maximum of an array of Ints 
[10,-22,753,55,137,-1,-279,1034,77].sorted().last 
[10,-22,753,55,137,-1,-279,1034,77].reduce(Int.min, max) 
[10,-22,753,55,137,-1,-279,1034,77].max()

#9 Parallel Processing

Some languages allows to enable in a simple and transparent way parallel processing of sequences for functionalities like map and flatMap, to speed up the execution of sequential and independent operations using thread pools under the hood.

This feature is not yet available in Swift but can be built using GCD: http://moreindirection.blogspot.it/2015/07/gcd-and-parallel-collections-in-swift.html

#10 Sieve of Erathostenes

The good old sieve of Erathostenes is used to find all the prime numbers until a given upper limit n.

Starting with a sequence of all the integers smaller than n, the algorithm removes all the multiples of each integer, until we are left with just prime numbers. And to speed up the execution, we don’t actually need to check each integer for its multiple, we can just stop at the square root of n.

Based on that definition the first implementation could look like this:


var n = 50 
var primes = Set(2...n) 

(2...Int(sqrt(Double(n)))).forEach{
     primes.subtract(Set(stride(from:2*$0, to:n, by:$0)))
}
primes.sorted()

We use the outer range to iterate over the integers we want to check and for each one we calculate a sequence of multiples of those numbers using stride(from:Int, to:Int, by:Int). Those sequences are then substracted from a Set initialized with all the integers from 2 to n.

But as you can see, to actually remove the multiples we use an external mutable Set, introducing a side-effect.

To eliminate side-effects, as we should always try to do, we will first calculate all the subsequences, flatMap them in a single array of multiples and remove these integers from the original Set.


var sameprimes = Set(2...n) 

sameprimes.subtract(Set(2...Int(sqrt(Double(n)))) 
                   .flatMap{stride(from:2*$0, to:n, by:$0)}) 
sameprimes.sorted()

Way cleaner and a nice example of usage of flatMap to flatten nested arrays.

#11 Bonus: Tuple swap via destructuring

As a bonus, not everyone knows that like in other languages that have a tuple type, you can leverage tuple destructuring to perform a compact variable swap:


var a=1,b=2

(a,b) = (b,a)
a //2
b //1

And that’s all, as expected Swift is as expressive as many other languages.

Thanks to @oisdk for reviewing the post

Did you like this article? Let me know on Twitter or subscribe for updates!

This is the blog of Umberto Raimondi, you can reach me at me@this domain.

I'm also on Twitter and GitHub.

Subscribe via RSS or email.