Instructor: Welcome back to continuing to fix the code
that we started to work on a bit earlier.
And so in this lecture, we're gonna focus on some of the,
functional and declarative principles
that we learned about in the last video.
And we're gonna focus on three big areas
of functional JavaScript just as we talked about
in the last lecture.
So that's first immutability,
second, side effects and pure functions,
and third making data transformations
using pure functions, such as map filter and reduce.
And let's actually start with immutability
because in JavaScript, there's actually a way
to make a data structure or in other words,
to make an array or an object immutable.
So let's take a look at that.
So let's go back here.
And so what we can do is to call the function,
object dot freeze.
Then in that function, we pass in the object
that we want to make immutable basically.
So by doing this spending limits is now immutable,
which means that we can no longer put any new properties
into it.
So let me show that to you,
but actually this works best in strict mode,
which I'm noticing here is actually not activated
in this script.
But we already know how to do that, right?
And so let's see what happens
if we try to add a new property to spending limits.
Let's say we want to add Jay actually,
'cause we have him down here.
And so let's say we want to actually allow him now,
to spend something as well.
So let's say 200.
And let's give it a safe.
And if we now check out this object,
then you see that it didn't change.
All right.
And so that is object dot freeze for you.
So whatever we try to add now to this object,
it's not going to work because it has been freezed.
So let's do the same with this one here,
because in the end, an array is also just an object, right?
And so we can also do object dot freeze on arrays.
Just like this.
And now we actually already start running into trouble
because down here we are trying to push something
into this array, which is now no longer possible,
because again, we made it immutable
by using object dot freeze.
So right out of the gate,
this code here is now no longer working.
Now, I actually wanted to show something to you.
So let me remove that.
But there's probably still gonna be an error down here.
Well, actually not.
And yeah, there's a reason for that,
which I'm gonna describe now.
So you saw that we can not add new elements
to this object now, right?
So that was the reason for the error that we just saw here.
However, object dot freeze here
basically only freezes the first level of the object.
So it's not a so-called deep freeze
because we can still change objects inside of the object.
So for example, I can do budget
and then the first element and then dot value.
I can set it to whatever I want.
And so if we see now the results here,
you see it that the value did indeed change, all right?
What I cannot do is to edit completely a new element.
So I cannot say, let's say object nine should be Jonas.
So that is not going to work.
So you see nothing was added here now.
All right?
So that's just something to keep in mind
when you use object dot freeze.
Now there are again, third-party libraries
which implement a deep freeze,
but I'm not gonna go that way here
in this small and simple example.
Let's just take out this one here and then let's keep going.
All right.
So let's put these back
and that will then bring back more error.
And so now we will need to fix this function.
So how do we fix this function
and why does it create the error in the first place?
So the problem is that right now, this function at expense
is trying to mutate this outside object.
So in other words,
according to what we learned in the last lecture
is that this function right now has a side effect, right?
So remember that a side effect basically means
that something outside of a function is manipulated
or that the function does something other than
simply returning a value, right?
And so a function that has, or that produces side effects
is called an impure function.
So this function at expense right now is an impure function
because it does attempt to manipulate
and to mutate this object that is located outside of it.
So how do we fix that?
Well, first of all, we should always pass all the data
that the function depends on into the function,
so that it doesn't have to reach into the outer scope.
So that's a first good practice.
And then of course
the function should not change any of these values.
So in other words, it should not mutate them.
And that's the whole reason
why we made this object here immutable,
just so we can not by accident, mutate this object.
Right?
So remember that the solution to that is to create a copy
and then return that copy of the state.
So of the data, right?
So I just said a lot of stuff.
So let's put all of that into practice here right now.
So the first thing that I'm gonna do
is to pass in a variable called state
and then also the limits here, okay?
So this state here will be the budget object
and then here the limits
is of course going to be the spending limits.
And so I will pass in all of that here
in all of the three function calls.
So budget and spending limits.
So that of course alone does not fix the error,
but we are working in the right direction.
Okay?
Now, I said in the last lecture
or in one of the previous ones
that we should not pass more than three arguments
into a function.
Now, in this case here, we actually have five parameters,
but well,
sometimes it's not a big deal to break those rules.
We could also pass in simply one object
of options basically, but let's keep it simple here.
I'm not into the mood of changing all of these inputs here.
All right?
So this is just fine here.
Then next up here we are
clearly manipulating the user variable.
So the user argument in this case.
And so as we learned in the last video,
we should avoid these data mutations whenever possible.
And so let's instead,
simply create a new variable right here.
So let's call this one here clean user,
because we are basically cleaning it.
This is a common terminology that we use.
So this is the whole reason why this Matilda here,
even with the capital M still works, okay?
So I didn't mention that in the previous lecture
where we worked about this, but without this piece of code,
it wouldn't work because Matilda here is uppercase,
but here in the object it is lowercase.
And so with the uppercase,
it will not be found here in the object
because this here is lowercase.
And so that's of course different.
And so here, we will always convert everything to lowercase
so that it then corresponds to the object keys
that we have here.
So this one here is from before.
Let's actually put it here where it belongs to.
So that was the alternative way of getting the limit.
Then here, we need to use that clean user and the same here.
So we still want this to be called user.
So here we need to go back to saying
user should be equal to the clean user.
Okay.
So we did most of the work here,
but now actually comes the main part.
And again, that is that we want to replace
this manipulating of the object by creating a new object
based on the state that we receive.
All right.
So let's get rid of this code here.
And instead, what do we want to do?
Well, we basically want to return an array like this here,
but simply with one more object.
So we learned already how to do that actually, previously.
And it's not very difficult.
So let's just say we want to return then an empty array.
And then we use the spread operator
to put all the elements of the state in there.
And so this effectively creates a copy of this state array.
Remember how we did that earlier?
And so this is exactly the same technique right here.
And now all we need to do
is to then add a new element right after this one.
And that element is gonna be just object,
which looks exactly like what we had before.
All right.
And so now we can get rid of this
All right.
So right now, calling the add expense function
will actually no longer mutate the budget object, right?
And so therefore,
if we want to then do something with the new budget,
we actually need to store that somewhere.
So let's say new budget one is equal to this.
Then let's quickly log it to the console as well.
And so let's see, and here we are.
So here is pizza.
So that's just the expense that we added here previously.
Great.
But now what happens if this here turns out to be false?
So basically if this code here does not run.
Well, let's try that by making this pizza here
ridiculously expensive.
And so you see that now we actually get undefined.
So that's not good, right?
And so what do you think we should return in this case?
Well, what we have to do is to return the original state.
So the original budget that we got in, right?
And so in that case,
the add expense function will always return something.
So it either returns the original budget,
or it returns the one with the new expense added to it.
All right?
So let's clean this up here a little bit
and simply use the ternary operator here,
because remember debt is a lot more declarative
than the more old school if else statement.
So here let's take away the return
and actually put it here in front.
So here we will now decide what is gonna be returned.
So in this case, what we will return,
is this new state.
So this new budget
or otherwise we will just return the original one.
Now we got some error somewhere.
It's this one.
And here we go.
So now we no longer got our error
and we are returning the original budget here.
And if we put it back to a normal price, then, here we go.
So here is our object or updated expense.
So our updated budget, but in a functional way.
So right now this function here,
it does no longer produce side effects.
It is now officially a pure function.
Let's actually write that here.
Because we're really happy about this now.
All right.
Now what about this one here?
So the next add expense call.
So do you think we still should pass
budget into this one here?
Well, not really, right?
Because this one will then again, act on the original budget
and that doesn't really make a lot of sense, right?
Because this previous expense that we just added
is not gonna be in there, is it?
So let me show that to you actually.
Put that down here,
and create a new one.
So let's see.
And so here now we have, going to the movies,
but indeed we don't have, the pizza here before.
And that makes sense, right?
Because we are again using the original budget,
which was never mutated by the previous add expense call.
Right?
And so what we have to do instead
is to here past in the results of the previous operation.
So that's new budget one.
So if we see now,
now it will have indeed pizza and going to movies.
And then of course the same thing here for the final
part of the puzzle.
So the final expense, the third one.
And here then we again use the previous one.
So that's new budget two.
But now this one here should be the same as before,
because of course Jay is not allowed to add anything.
All right.
So only pizza and going to movies has been added there.
Let's make this one here a bit smaller
so that it fits in one line.
Well, it didn't.
All right.
So let's just leave it like that.
And yeah, so that first part here is now fixed.
Now in the real world,
we would use something called composing
and the technique called currying
to basically create this chain of operations here.
So here we need all of these intermediate variables
to create a new budget, right?
So we call this once store the result in a new variable,
then the next call we use that previous variable,
and then again, in the next call,
we use that previous variable again.
So this is kind of a chain of add expenses,
and it would be nice to always automatically
use the previous result for the next operation.
And so again, in a real world, big functional application,
we would use composing to create one function,
which will then perform all of these operations at once.
But that would really be outside the scope of this course,
because that is a whole big world on itself.
So here we are just trying to implement
some of these techniques and some of these concepts,
just so you see how they work and that they exist
and that they are important.
And again, this becomes especially important
once you start using one of the frameworks out there
just like react or Redux, which rely heavily on concepts,
such as immutability and pure functions.
But anyway, let's now move on here in our code.
And now that we already talked so much about side effects
and immutability and pure functions,
let's turn our attention to data transformations
here in this case.
Because as you see here we have this for off loop,
which we can easily transform
into a functional function basically.
So we have this loop here,
which loops over all the entries in the budget, right?
So basically which loops over this array
and in each iteration,
the current one is one of these entry objects.
And then in this loop, it will update each of the object,
to contain the flag attribute basically
whenever the value is over the limit.
So that happened for example, here.
And so we got flag set to limit.
Remember that from the previous lecture.
But of course this function is again, an impure function
because it does manipulate the object itself, right?
So in this case, it works with a budget,
but the same would be true if we wrote new budget
three here, which was our latest and updated budget.
So this one yhere actually come from line 65.
So it's this one.
So what I would be interested here is this.
So let's move that down here.
And so here indeed, we will now see...
Yeah, so here is flag set to limit.
And so we violated the principle of immutability here,
right?
And therefore let's transform this function here
into a pure function as well.
So again, we will want to pass in all the data
that the function depends on.
And so that's, again, well in this case,
it is budgets three.
So new budget three,
and also we actually need the limit from the outside.
And actually we skipped this function here,
but let me come back to that in a minute.
So let's just pass in the spending limits here as well.
So let's go back up here.
So we passed in the spending limits here
into the add expense function,
but we actually never used it.
And so the reason for that is that get limit here,
actually, uses the spending limits from the outer scope.
And that's something that we do not want, right?
And therefore let's add the limits here,
as another parameter.
Limits and user.
And then here, I can just call it, limits like this,
and then here I pass the limits in.
All right.
So it works just like before, but now this function here,
no longer depends on any external variable.
It can do all its work without having to look up
in the scope chain.
Okay.
So let's do the same here as well.
So here we want to then receive a state.
I'm gonna call it state to keep it neutral, just like before
but then calling it limits once again.
So here again, we want to pass in limits
and then the user itself.
But now let's then finally actually replace this entire loop
and instead loop over the state,
using one of our data transformation functions.
So what do we want to do here?
Well, we want to basically keep an array
of the same size here in the end, right?
So, in each of these objects,
we basically just want to add a property,
but that's all we want to do.
We don't want to filter anything
or we don't want to use reduce either, right?
So it makes sense to use the map method here.
And map, remember, will create a brand new object.
So brand new array.
And so that's exactly what we want, right?
So that's in the spirit of functional code
and of immutability.
So not mutating the state,
but instead creating a new state based on the original one.
And so, in fact, this is then the state
that we are going to return from this function.
But let's not get ahead of ourselves
and instead work here on this.
So let's create a function block here
and think about what we want to do here.
So right now here inside of this callback,
each entry is one of these objects, just like this, right.
But of course we still do not want to mutate
any of these objects, right.
Instead when this condition here is true,
so when the value is less than the limit,
then essentially we want to copy the object
and add the new property onto that copy.
All right.
So basically just like we did here.
So the idea is the same.
So here we copied the entire array
and then added a new object.
But here, what we will do is to basically copy
the entire entry object
and then add the flag property to that.
So that sounds a bit complicated,
but trust me, it will all make sense.
So let's copy this condition here, just like this.
And then again, making the code nicely declarative
by using the turnery operator.
So if this condition is true.
So if the value is above the limit
then, here we want to return the original object copied.
So remember that this creates a copy and add a new property.
And so that's flag set to limit.
And otherwise what we will want to do is simply return
the original entry.
And that's actually it.
So let's just check out the result
and then, I will show it to you again.
All right.
So here now, of course, we need to again,
store the results into a variable.
Let's call this one final budget
because we will not create any new budgets.
And I'm then gonna log that here.
And so let's see if we got the flag here.
And indeed flag is set to limit.
So that worked and you see that we did not
actually manipulate any object.
All we did here was to create a copy
and then add the property onto that copy, right?
Now here, you might not be used anymore
to actually see the return here
in the map callback function,
but that's just because we have this function block here,
right?
And so remember that in the map function,
whatever is returned from the callback
will be the element in the same position of the new array,
right?
So that's why we need the return here.
But of course we could also
completely simplify all of this here
if we wanted to make it simpler.
So let's take this and actually transform it
into an arrow function if you prefer that.
I will then leave these two here.
So function and state, then let's say, just like this,
then we don't need the return.
And here we can get rid of the braces as well
and of the return.
Let's call this one here two.
And here we don't need that semi-colon I guess.
Now that's it.
Now we're ready to go.
And this is basically the same function.
Just copy it, or actually commented out.
So just in case you prefer this arrow function.
So I hope that this made sense.
What's really important to note here once more
is that we transformed this function here
into a pure function, which does not mutate anything
because the map method here returns a brand new array.
So we give this function an array
and it will then create a new one
simply by mapping over the original one,
which again, creates then a brand new one.
And in each position of the array,
we then either return a copy of the original entry
plus the flag property,
or simply we return the original entry as it was.
And so with this, again, our function is nice and pure
and does not create any side effect
and does not manipulate anything.
Nice.
And maybe you're actually noticing that throughout,
most of the course,
we actually kind of followed these principles already,
but without calling it functional.
And I remember, especially the bankers project.
So in a section about arrays.
So in there, I told you a lot of times
that we should always pass all the data that we need
for a certain function to work right into that function.
So that, again, it does not depend on any outside data
because really you will see in practice
that this makes it a lot easier
to reason about the function itself.
And so if you do that for all of your functions,
then in the end that will make your code
a lot easier to understand and a lot more readable.
Okay.
But with that being said,
let's now move on here to our final function.
And here again, we have this for loop
and inside of that loop,
we are actually constantly manipulating
or mutating this output variable here.
And that of course, goes against the spirit of immutability.
Right.
So immutability is of course
not just for objects and arrays.
This also goes for more regular variables.
And so in functional code,
you will probably never see the let variable.
So we're always trying to compute a variable,
for example, based on methods like this one here.
All right.
And so here we will actually be able to achieve
something similar.
So let's think together how we could
create a string like this here
simply based on pure and functional functions.
So, data transformation functions for arrays,
and also for strings actually.
Let's comment all of this out
because of course there is gonna be a better,
more declarative and functional way.
So in the code that we had here before,
of course it was very imperative.
We said, create an empty variable.
Then we manually said that it should loop over
all of the entries in the budget array.
And then whenever we had a big expense,
which is basically this one here,
it should then add the emoji to the output variable here.
So to this string.
All right.
But if we really think about this,
there is a better way to doing this.
So this one here is called output,
and I guess we should just actually call it big expenses.
So we could have done that before.
So giving it a better name.
So what we really want to do,
and this now I think requires a little bit more thinking
on our part, but believe me is gonna be worth it.
So what we really want to do here
is not all of this imperative stuff.
So what we want to do is actually just filter our array
for these big expenses.
And then basically for each of the results,
all we want to do is to create a nice string
containing only the emoji.
So let's translate what we just said
and starting with the filter.
So entry,
and then here, the condition is of course gonna be the same.
So basically, an expensive or a big expense.
So, let's start by, logging this here
just so we can see what we're doing.
Big expenses.
And here we say that the state is not defined
and that's simply because I didn't pass it in yet.
So just like before,
of course we want to make this function
not to depend on any variable from the outside.
That's how we pass it in here as final budget.
So let's see what we have here.
And indeed that is the new iPhone and laptop,
which are exactly or big expenses.
And so now all we need to do is to get these emojis here out
and then create a nice string based on the result.
All right.
So we have now this array of two.
And so we want to, again, create an array of two,
but only with these emojis.
So let's use a map for that.
So again, there's an entry.
And here we want the entry dot description
and then just take the last two characters.
So just like we did here before,
All right.
And now we can just join that
like this as we had before, and there we go.
So this here is essentially the functional version
of just doing this here.
So, after this video, you can maybe take a better look
and really compare these two things that we did here.
Let me just show you that we could also have done it,
in another way.
So instead of map and join,
we could have done it all in one go using reduce.
And so once again, this is a nice use case
of showing you a more advanced way of using reduce.
So again, the use case of reduce
is to basically take all the values in one array
and create one value out of them.
So reduce them all into just one.
And in this case, what we want is our string.
And so that's gonna be the accumulator.
And then as always, we have the current value,
then here a callback, and then the initial value,
which is just gonna be an empty string.
So what we want to do here is to basically always return
the previous string plus the current string.
So that's the previous string and then the current string,
like this.
And of course, we still need to say
dot description dot slice, like this.
So we're almost there already, just missing this separator.
And there we go.
Now we just have this ugly thing here.
Let's see what happens if we remove this here altogether.
And yeah, of course that cannot work.
We always need an initial value.
And so now we could go ahead and only start reading here,
but that's not really necessary.
So we can of course go back to using just this code,
but that's not gonna be necessary.
Of course, we can just go back and use this code right here
and take out this one.
So this was just, yet another use case of the reduced method
that I wanted to show you here.
So that we can create even the strings using reduce.
All right.
So I'm leaving this here again for you to compare,
give it a save and I think that with this, we are done.
So we transformed our initial code, which looked pretty bad
first by applying some general guidelines
for a modern and clean code.
And then now in this video, we made our code functional
and took out all of the impure functions
and side effects and data mutations.
So a lot cleaner, a lot nicer.
And also if you ask me a lot more professional looking code.
Now, actually this function here is not pure.
Let me just write that here.
So it's an impure function because it creates a side effect
by doing this console dot log here.
Okay?
So in fact, all console dot logs here,
are of course impure because they do something.
So they create something here in the console in this case.
So they create input output.
But of course, any program needs to have some side-effects
because otherwise,
what is the point of the program in the first place?
If we didn't have all of these console dot logs,
how would we even know that the program
is running in the first place?
All right?
It would not be useful at all.
And so, as I said, we always need some side effects,
but in functional programming,
we try to push these side effects as far to the edge.
So as far to the end of our program as possible.
So without having them all over the place,
polluting our application.
Now this, kind of mini project here is of course,
very far from being complete.
And if you wish
you can of course develop this a lot further.
For example, you can add a function for adding the income,
or you can also create functions
for calculating the total expenses and incomes,
the overall budget,
how much the expenses are in percentage of the income
and so on and so forth.
But I will leave it like this here,
because again, this is enough for us now.
And our code looks pretty professional as it is.
Just keep in mind again, that these are more like guidelines
and not really hard rules.
So large applications,
they are very hard to make 100% functional.
And that's absolutely okay.
So actually right in the next project,
we will already break many of these rules.
And again, that's okay.
It's not a big problem.
As long as we try to still keep some of these principles
in some of the places we're already making our code
a lot better and a lot more professional.
Okay.
And with that being said, you're now ready to go build
the biggest and kind of the final project of this course
in the next section.
And so I really hope to see you there soon.