What I learned last week - 16 December 2019
Tags: ios, development, swift, functional
Published: 2019-12-16 18:25:34

---
title: What I learned last week - 16 December 2019
published: true
description: Challenges I faced this week in iOS Development
tags: iOS, development, Swift, Functional Programming
---

##Introduction##

When I first started learning iOS development, I blogged weekly about what I was learning. Once I completed my bootcamp, I fell away from it as I was just trying to stay afloat at work. 

However, blogging helped me retain a lot of information, and lately, I feel like I figure something out, feel happy, but then promptly forget it and a few weeks later, have to do the same thing again, and try and relearn. So this is my attempt to pick the writing habit back up. I'll just write about a challenge, or learning opportunity I faced and how I went about solving it, or in same instances, re-explain how it was explained to me--if for nothing else, but to reinforce the concept.

###Challenge - Nested Data###

A current feature I'm working on at work is to display a rate schedule for plumbers to view when installing a water heater. This schedule is a cost structure of the cheapest and most expensive times to heat water throughout the day, and their associated rate per kilowatt-hour (kWh).

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/o47y4awn0nz22i9lmyod.png)

The data we receive with the rates, times, seasons, etc... is...involved. There can be multiple schedules. Within each schedule, there can be multiple seasons. Each season has a two time periods. Within the time period, there can be anywhere from 1 - 5 rates and 1 - 5 times.

I've pared down the actual data into what I'm using just for this blog post, but below is a visual representation of how deeply nested the data coming back is.


~~~swift
struct UtilityScheduleResponse {
	let schedule: [UtilitySchedule]
}
struct UtilitySchedule {
	let scheduleID: String
	let scheduleName: String
	let seasons: [UtilitySeason]
}

struct UtilitySeason {
	let seasonID: String
	let touSeason: String
	let seasonName: String
	let seasonDescription: String
	let timePeriods: [TimePeriod]
}

struct TimePeriod {
	let periodID: String
	let periodName: String
	let periodDescription: String
	let tou: [TimeOfUse]
}

struct TimeOfUse {
	let touID: String
	let touName: String
	let touStartTimeFormatted: String
	let touEndTimeFormatted: String
	let touRate: String
}
~~~

However, for the purposes of displaying all rates and times, there is just a few items of data I need to throw into the `RateScheduleCell`.

So I started to dive into the data into the first way that popped into my head...Nested loops.

#####Attempt 1#####
~~~swift
func nestedScheduleDetailsExample(for schedule: UtilitySchedule) -> [RateDetail] {
	var rateDetailsArray: [RateDetail] = []
	for season in schedule.seasons {
		for timePeriod in season.timePeriods {
			for timeOfUse in timePeriod.tou {
				let rateDetail = RateDetail(rate: timeOfUse.touRate,
											startTime: timeOfUse.touStartTimeFormatted,
											endTime: timeOfUse.touEndTimeFormatted)
				rateDetailsArray.append(rateDetail)

			}
		}
	}
	return rateDetailsArray
}
~~~

Hmmm...okay, it works, but I know I need to work on functional programming and there has to be a way to do this functionally.

#####Functional Attempt 1#####
~~~swift
func functionalAttempt1(for schedule: UtilitySchedule) -> [RateDetail] {
	var rateDetailsArray: [RateDetail] = []
	for season in schedule.seasons {
		for timePeriod in season.timePeriods {
			rateDetailsArray = timePeriod.tou.map({ (tou) -> RateDetail in
				return RateDetail(rate: tou.touRate,
								  startTime: tou.touStartTimeFormatted,
								  endTime: tou.touEndTimeFormatted)
			})
		}
	}
	return rateDetailsArray
}
~~~

So this worked...sort of. All I ever got was the last rate detail in the json. This is because I failed to realized that after the first iteration of timePeriod in season.timePeriods, the rateDetailsArray would be overwritten with the next timePeriod. So while the map was working to turn the `timePeriod.tou` array into an array of RateDetail objects, it was removing the previously created ones on each iteration in the `timePeriod` for-in loop.

Okay, so now I need an array of timePeriods with its own array of RateDetail objects. So I rewrote the function.

#####FunctionalAttempt 2#####

~~~swift
func functionalAttempt2(for schedule: UtilitySchedule) -> [[RateDetail]] {
	var rateDetailsArray: [[RateDetail]] = [[]]
	for season in schedule.seasons {
		rateDetailsArray = season.timePeriods.map { period in
			 period.tou.map { timeOfUse in
				RateDetail(rate: timeOfUse.touRate,
						   startTime: timeOfUse.touStartTimeFormatted,
						   endTime: timeOfUse.touEndTimeFormatted)

			}
		}
	}
	return rateDetailsArray
}
~~~

But I didn't really wanted a nested array of objects. I just wanted an array of all the RateDetails. I was stuck. So I turned to the Slack boards to ask for help in making my function more...well, functional...but also to have a single array.

Almost immediately, I got several responses. I guess I knew this at one point and forgot, but `flatMap` will flatten an array, meaning taking a nested array and concatenating the result into one array. Mostly, when I thought of `flatMap` I remember it used to be a way to return an array that excluded nil values. 

When Swift 4 was released, `compactMap` took over that role and I guess I thought 'flatMap` had been deprecated. 

__compactMap__
> Returns an array containing the non-`nil` results of calling the given transformation with each element of this sequence. Use this method to receive an array of non-optional values when your transformation produces an optional value.

__flatMap__
> Returns an array containing the concatenated results of calling the given transformation with each element of this sequence. Use this method to receive a single-level collection when your transformation produces a sequence or collection for each element.

Turns out, `flatMap` was deprecated for the non-nil role, but is still alive and kicking doing what it's name implies...flattening an array.

For me, just being told to use something often leads to paralysis. I have an answer, but I'm not sure how to use it. Thankfully, everyone I talked to on Slack gave some examples of the use of `flatMap`.

#####Functional Attempt 3#####
~~~swift
func functionalAttempt3(for schedule: UtilitySchedule) -> [RateDetail] {
	let rateDetails: [RateDetail] = schedule.seasons
		.flatMap { (utilitySeason) -> [TimePeriod] in utilitySeason.timePeriods }
		.flatMap { (timePeriod) -> [TimeOfUse] in timePeriod.tou }
		.map { (tou) -> RateDetail in RateDetail(rate: tou.touRate,
												 startTime: tou.touStartTimeFormatted,
												 endTime: tou.touEndTimeFormatted)
	}

	return rateDetails
}
~~~

Okay, so there is a lot to unpack here. `rateDetails` has a type of `Array<RateDetail>`, so the final part of that code better be creating instances of `RateDetail` and appending it to the array.  Everything leading up to it is diving through the UtilitySchedule to find the TimeOfUse array which contains instance of TimeOfUse. Each instance is then used to create a RateDetail.

I've purposely left the longer notation for map and flatMap because, for me, I prefer the readability.  I know what kind of object each closure is taking in (i.e `UtilitySeason`, `TimePeriod`, `TimeOfUse`), and what the expected output at the end of each flatMap or map call should create (i.e. `Array<TimePeriod>`, `Array<TimeOfUse>`, `Array<RateDetail>`.  If I had not used flatMap, I'd have something like:

`Array<UtilitySeason<Array<TimePeriod<Array<TimeOfUseArray<Array<RateDetail>>>>` 

or to see it visually another way,

`[[[[RateDetail]]]]]`

I think we can agree a completely mapped version is pretty ugly and unwieldy to navigate through.

By using `flatMap` on the first three arrays (UtilitySeason, TimePeriod, and TimeOfUse), I'm able to flatten out the entire thing and just map the part I want to transform into one single array of `RateDetail`. This makes setting my tableView cell much easier and less error prone because lord knows trying to set the prices with something like

`cell.weekdayPrice.text = utilitySchedule[0][0][3][1].rate` 

would have been just asking for disaster.

I am so grateful for the help on Slack. Every time I've ever asked a question, it's always been met with genuine interest in helping me. I've never been belittled for not knowing something, or ridiculed for asking what may seem like a "simple" problem. Everyone gets stuck on things sometimes, or has a hard time seeing through an answer. In the end, I refactored my models some so I would not have to write this function in the ViewController, but I think I need to continue to remind myself to START SOMEWHERE! I can always make it prettier later...but at the very least, make an attempt to solve it rather than trying to do it in my head and hoping to write the perfect algorithm on the first try.


Update: I also want to thank Donny Wals @DonnyWals for editing this for me. He gave me some helpful insight and was a wonderful sounding board.