What I learned last week - 23 Dec 2019
Chris Myers
Posted on December 23, 2019
So this week was more organizing, filtering, and using of data from the model I mentioned last week. A couple of updates...
Update from last week
First, the RateDetail
array that I created by flattening the arrays it was nested within was a good first start. In the end, I wound up creating a dictionary with the array of rate detail as the value, and the schedule it was related to as the key so that I could provide my view with the necessary information. In order to do that, I need the schedule name at one level of the nested array and then build the array of RateDetails in the very bottom or last level.
I wound up going into my UtilitySeason
model and creating a variable called seasonRatesDictionary
which is a dictionary with a String
for the key and Array<RateDetail>
for the value. The rate details sit inside the another model called TimePeriods
, which just happens to be a property within UtilitySeason
struct UtilitySeason {
let seasonID: String
let timePeriods: [TimePeriod]
}
~~~{% endraw %}
Inside of {% raw %}`TimePeriod`{% endraw %} sits the {% raw %}`TimeOfUse`{% endraw %} property, which is where the information needed to build the instance of {% raw %}`RateDetail`{% endraw %} sits.{% raw %}
~~~swift
struct TimePeriod {
let periodName: String
let tou: [TimeOfUse]
}
~~~{% endraw %}
With this information, I can build my dictionary.{% raw %}
~~~swift
var seasonRatesDictionary: [String: [RateDetail]] {
let tempDict = timePeriods.reduce(into: [:]) { (dict, timePeriod) in
dict[timePeriod.periodName] = timePeriod.tou
}
let periodsDictionary = tempDict.mapValues { (touArray) -> [RateDetail] in
touArray.map { (touInstance) -> RateDetail in
RateDetail(rate: touInstance.touRate,
startTime: touInstance.touStartTimeFormatted,
endTime: touInstance.touEndTimeFormatted)
}
}
return periodsDictionary
}
~~~{% endraw %}
As it turns out, {% raw %}`reduce`{% endraw %}, which I always associated with just adding stuff together since that's the default example you see in explanations of it, can be used to build a dictionary. The first parameter in the closure refers to the empty dictionary that I created in the {% raw %}`into: [:]`{% endraw %} part of the reduce function. The second parameter refers to each timePeriod inside the {% raw %}`timePeriods`{% endraw %} property of my {% raw %}`UtilitySeason`{% endraw %} model. I'm building a temporary dictionary first to set all the keys in the dictionary with the value of its respective {% raw %}`TimeOfUse`{% endraw %} array, and then make use of the built in {% raw %}`mapValues`{% endraw %} function associated with Collection-conforming types to go into that temporary dictionary and create my {% raw %}`RateDetails`{% endraw %} array for the returning dictionary.
I chose to build this in the model because having to dive a few layers in to extract values out, in my opinion, should be the work of the model. It has layers of information, and I think should be able to give me what I need so that I don't have to write some function to do it and busy up my ViewController.
####New Learning'####
However, what I learned this week that really blew my mind was something called composition.
Here is what I was trying to do...{% raw %}
~~~swift
for dict in arrayOfScheduleDictionaries {
request.addBodyPairs(dict)
}
~~~{% endraw %}
I have to send some data out in a POST request. It just happened to be the dictionary I was building earlier. The framework we use to do this allows us to append dictionaries to the body of the POST using {% raw %}`addBodPairs`{% endraw %}
Now I know the for loop above works, but since I'm trying to incorporate a more functional approach to my code, I settled on this...{% raw %}
~~~swift
let _ = arrayOfScheduleDictionaries.map { request.addBodyPairs($0) }
~~~{% endraw %}
It isn't very often I have an unassigned variable (i.e. {% raw %}`let _ `{% endraw %}) so I turned to Slack and asked if there was a better way of writing this.
While it might be personal preference, the code above does work, but the overwhelming opinion was that "if you are writing {% raw %}`let _` on the left side of your `map`, then that should be a hint that `map` isn't the right tool for the job." Instead, it was suggested that I try {% raw %}`forEach`{% endraw %}.{% raw %}
~~~swift
arrayOfScheduleDictionaries.forEach { request.addBodyPairs($0) }
~~~{% endraw %}
It does what I want it to do, add each dictionary to the network request I am making, without resorting to using {% raw %}`_`{% endraw %} for my unassigned variable.
Now I was ready to go and make use of this new found knowledge, but another poster mentioned that it can be taken a step further, by calling it "point-free". I asked what this was and was told that the tl;dr version is that it avoids creating another function to call a function.
A slightly more in-depth version is that in my example above, point-free means I don't have to reference {% raw %}`$0`{% endraw %} in the {% raw %}`addBodyPairs`{% endraw %} function.
I was pointed to an article about point-free and the opening sentence states:
>It is very common for functional programmers to write functions as a composition of other functions, never mentioning the actual arguments they will be applied to.
I will admit that this goes beyond my knowledge and I still don't really understand it. But I do know that Object-oriented programming uses composition with classes, and in Swift's case, structs and enums. Protocols are a good example. In a protocol, I don't really define the implementation, I just define the name, parameters and return value of the function that the object conforming to the protocol must implement.
In this case, I gather the same is true of the functions. {% raw %}`forEach`{% endraw %} wants a function of the type {% raw %}`(T) -> Void`{% endraw %}. It doesn't care what {% raw %}`T`{% endraw %} is, it just wants to accept that function type.
Coincidentally, {% raw %}`addBodyPairs`{% endraw %} is a function of type {% raw %}`(T) -> Void`{% endraw %}. So I can use it as a parameter in the {% raw %}`forEach`{% endraw %} function.
Admittedly, I still didn't understand how addBodyPairs was getting a reference to the dictionary in {% raw %}`arrayOfScheduleDictionaries`{% endraw %} because reading a wikipedia post on composition just confused me further.
Fortunately, in another time, I used to be a high school economics, as well as computer programming teacher, and I was constantly telling my students that the internet will help them find answers. My favorite recommendation was [Khan Academy](https:www.khanacademy.org). The videos are short, descriptive, and come chock full of examples and sample problems. So I turned to the video to explain mathematical composition to me so that I might understand programming composition better.
[Khan Academy Video on Composition](https://bit.ly/2ZkesqI)
It helped. I still need to think about it more to see if I can explain it, and come up with a couple examples on my own, but it appears that the functions keep passing themselves back to the left to the previous function until there is a value to use. In our case, it's each dictionary in the {% raw %}`forEach`{% endraw %}. From there it can evaluate.
In the end, the call ended up like this:{% raw %}
~~~swift
arrayOfScheduleDictionaries.forEach(request.addBodyPairs)
~~~{% endraw %}
That's a lot to write about just to be able to remove {% raw %}`$0`{% endraw %} but as I said before, this blog is document what I'm learning--even if I don't have the clearest understanding of it yet.
Posted on December 23, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.