What I learned last week - 23 Dec 2019

myerschris78

Chris Myers

Posted on December 23, 2019

What I learned last week - 23 Dec 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. 
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
myerschris78
Chris Myers

Posted on December 23, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related