Higher Order Function and its uses in Swift 4



If you are working in Swift for sometimes you definitely heard about higher order functions which are mainly used for functional type programming.

Here I am going to introduce few of them to you

Update

With latest release of Xcode 9.3 and Swift 4.1 the “Sequence.flatMap(_:)” is deprecated and is renamed as “compactMap(_:)

1: Map()

the map is a function type used with emulator objects like an array or sets. It can apply a function to every individual object in an emulatable object

let’s take an Example of string Array

let houseName:[String] = [“starks” ,”lanniesters” , “targaryens” ,”baratheon” , “arryn”]

say now we need to count the number of texts in each element and we need that result in the corresponding array

func characterCount(house:[String]) -> [Int]
{
var characterCountArray = [Int]()
for item in house
{
characterCountArray.append(item.count)
}
return characterCountArray
}
let houseCharacterCount = characterCount(house: houseName)
print(" Characters count of each element in house name :- \(houseCharacterCount)")

the above code will print

Characters count of each element in house name :- [6, 11, 10, 9, 5]

In above you are writing your function where the array is passed and then items are counted

But in case of the map, everything can be done in a single line

let mappedHouseCount = houseName.map{$0.count}

Let’s verify both results

let check = houseCharacterCount == mappedHouseCount
print(Check)

this will print “True”, where you are getting the similar result just with a single line of code by using map.

The Map can be used to any function, let’s say we need all uppercase letters.

let upperCaseHouse = houseName.map { $0.uppercased()}
print(upperCaseHouse)

the above result will be printed as shown below

[“STARKS”, “LANNIESTERS”, “TARGARYENS”, “BARATHEON”, “ARRYN”]

Maps can also be associated with the user-written function

let’s take a number array where we need to find factorial for each number

let numberArray:[Int] = [2,5,10,15,20]

So we create below extension to find the factorial of each number in numberArray

extension Int
{
func factorial() -> Int
{
var fact: Int = 1
for i in 1…self
{
fact = fact * i
}
return fact
}
}
let factorialResult = numberArray.map{$0.factorial()}
print(factorialResult)

the above result will print as

[2, 120, 3628800, 1307674368000, 2432902008176640000]

Maps can also be used for a boolean decision like as below

let grade:[Int] = [30,45,50,100,12,28,46,31,34]
let boolVal:[Bool] = grade.map{$0 >= 35 ? true :false }
print(boolVal)

the value of bool will be like

[false, true, true, true, false, false, true, false, false]

Note:- In map the order of execution is not guaranteed in sequence but as in case of for-each, the order is guaranteed.

houseName.forEach{print($0)}
let upperCaseHouse = houseName.forEach {$0.uppercased()}

2: Using compactmap()

compact-map is same as the Map function with optional handling capability

let place:[String?] = [“winterfell” , “highgarden” , “Vale” , “iron islands” , “essos” ,”andalos”]
let printValue = place.map{$0}
print(printValue)
[Optional(“winterfell”), Optional(“highgarden”), Optional(“Vale”), Optional(“iron islands”), Optional(“essos”), Optional(“andalos”)]

the printed values have optionals among it where this can be avoided using compactMap

let compactMapValue = place.compactMap{$0}
print(compactMapValue) // here the optionals are removed
["winterfell", "highgarden", "Vale", "iron islands", "essos", "andalos"]

compactMap is also used to filter out the nil value

let arrayWithNil:[String?] = [“eleven” , nil , “demogorgon” , nil , “max” , nil , “lucus” , nil , “dustin”]
let filteredNilArray = arrayWithNil.compactMap{$0}
print(filterNilArray)
print (“Array with nil = \(arrayWithNil.count) and with out nil count = \(filterNilArray.count)”)

the printed values are

[“eleven”, “demogorgon”, “max”, “lucus”, “dustin”]
Array with nil = 9 and with out nil count = 5

compactMap will come in handy when you need a check on nil or be working on optional type like below, where you need to convert a string to an integer where converted value is optionals.

let numArray:[String] = [“45” , “60” , “75” , “something random error” , “15” , “Another Error”]
let integerArray:[Int] = numArray.compactMap{Int($0)}
print(gradeActualArray)

the result of an integer is obtained by removing out errors

[45, 60, 75, 15]

3: Using filter()

the filter is used to select the explicit item based on condition.

let numbers = Array(1…100)
let evenNumbers = numbers.filter{ $0%2 == 0}
print(evenNumbers)

the filtered array is as below

[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100]

Filter function to check for string Value

let name:[String] = [“jon snow” , “Arya Stark” , “Jamie Lanniester” , “Sansa Stark” , “Renly Barathon” , “Catelen Stark” ,”Theon Grayjoy” ,”Ned Stark”]
let starkFamily = name.filter{$0.hasSuffix(“Stark”)}
print(starkFamily)
[“Arya Stark”, “Sansa Stark”, “Catelen Stark”, “Ned Stark”]

Another example of the filter

let arrayWith_min_10_char = name.filter{ $0.count > 10}
print(arrayWith_min_10_char)

here the name with character count greater than 10 is filtered and printed as below

[“Jamie Lanniester”, “Sansa Stark”, “Renly Barathon”, “Catelen Stark”, “Theon Grayjoy”]

Note:- Here whiteSpace is also considered as one character

4: Using sorted()

sorted is used to rearrange the elements in the Array

let randomNumbers:[Int] = [1 ,3,45,6743,4673,435,4162,6657,2431,658,686,56,3456,8875,325,46,2,66537,6]

simple sorting of the number

let sortednumber = randomNumbers.sorted()
print(sortednumber)

the sorted array numbers are

[1, 2, 3, 6, 45, 46, 56, 325, 435, 658, 686, 2431, 3456, 4162, 4673, 6657, 6743, 8875, 66537]

Same thing can be done for strings

let alphabets:[Character] = [“V” ,”I” ,”S” , “H” ,”W” ,”A” ,”S” , “v” ,”i” ,”s” ,”h” , “w” ,”a” ,”s”]
let sortedAlphabets = alphabets.sorted()
print(sortedAlphabets)

the sorted string Array output as below

[“A”, “H”, “I”, “S”, “S”, “V”, “W”, “a”, “h”, “i”, “s”, “s”, “v”, “w”]

Note: The strings are sorted based on their ASCII Value A-Z(65–90 ) and a-z(97–122)

few other examples based on sorting

let evenFirstSorted = randomNumbers.sorted 
{ (a, b) -> Bool in
return a % 2 == 0
}
print(evenFirstSorted)
[6, 2, 46, 3456, 56, 686, 658, 4162, 1, 3, 45, 6743, 4673, 435, 6657, 2431, 8875, 325, 66537]

sorting can be done based on <(lesser than), > (greater than)

let greaterThanArray = randomNumbers.sorted(by: >)
let lesserThanArray = randomNumbers.sorted(by: <)
print(greaterThanArray)
print(lesserThanArray)
// greater than
[66537, 8875, 6743, 6657, 4673, 4162, 3456, 2431, 686, 658, 435, 325, 56, 46, 45, 6, 3, 2, 1]
// lesser than
[1, 2, 3, 6, 45, 46, 56, 325, 435, 658, 686, 2431, 3456, 4162, 4673, 6657, 6743, 8875, 66537]

5: Using reduce()

https://developer.apple.com/documentation/swift/array/2298686-reduce

reduce is used to combine all element in Array to make one single value.

let sumOfNumbers = numbers.reduce(0,{$0 + $1})
print(sumOfNumbers)
5050

here the initial value ($0) will be 0 and then the value from num array is added to the initial value, which is retained for next iteration. In the end, one single value is returned

The same thing can be obtained with string Array, where all string element is merged into one single string

let nameString = name.reduce(“”, {$0 + $1.replacingOccurrences(of: “ “, with: “”)})
print("type of name :- \(type(of: name)) , type of nameString :-  \(type(of: nameString))")

We can see the difference between name and name string below

type of name :- Array<String> , type of nameString :- String

Other few examples for Reduce

let stringOfNumbers = numbers.reduce(“”, {String($0) + String($1)})
print(stringOfNumbers)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100

count all the characters in a array

let nameArrayCharactersCount = name.reduce(0, {$0 + $1.count})
print(nameArrayCharactersCount)
94

Find Longest Name

let longestname = name.reduce(“”, {$0.count > $1.count ? $0 : $1 } )
print(longestname)
Jamie Lanniester

These higher-order functions can be applied in lot other scenarios when you are working with collections object.

for example playground file you can refer

Check out my next Article