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.
Top comments (0)