Check out a free preview of the full Deep JavaScript Foundations, v3 course
The "Equality Exercise Solution" Lesson is part of the full, Deep JavaScript Foundations, v3 course featured in this preview video. Here's what you'd learn in this lesson:
Kyle codes the solution to the exercise.
Transcript from the "Equality Exercise Solution" Lesson
[00:00:00]
>> Kyle Simpson: All right, welcome back from that. I trust that you found that satisfyingly challenging to deal with the various corner cases. So let's dive in and try to write a findAll utility. And fair warning, I'm definitely gonna have to look at my own solution cuz I'm sure I'm gonna miss one of these cases and fumble around with.
[00:00:21]
All right, so the signature of findAll is that we wanna take in a value, I'll call it v, and we wanna take in a list, I'll call it arr. And actually let's call that not v, let's call it match, that's the thing we're matching against, arr is the list of values to pull from.
[00:00:38]
We're supposed to return back an array. So we'll start out with an empty array and return that. And then we need to loop through all of the values in arr.
>> Speaker 2: I think you're returning an empty array.
>> Kyle Simpson: Sorry, thank you. We want to return all of the values in arr that match our.
[00:01:03]
So I will do a for of loop just for simplicity. We'll loop over all the values in arr. And if we find any, we'll stuff into this return array and then return them, okay. So fairly straightforwardly the first test is, if v and match are object.is of each other.
[00:01:26]
In other words, they are strictly exactly the same, so that's a pretty easy one. If Object.is and we use match and v. We know that they are definitely the same value, so we can just push into the return array. I'm sorry, v into the return array. Let's just stop right here and make sure that we haven't missed anything.
[00:01:54]
Let's try this out in our particular test cases. And we should see a few test cases pass and then several of them obviously still failing. So let's try that. And this little environment I have doesn't like that function at the bottom, so I will move this up here just temporarily.
[00:02:15]
Okay, so we run this. And you'll notice that the first three are failing still, but then the next two pass, and then we get several more that pass. So we did pretty good. We got several of these returning what we would expect already. Let's switch back then. And the next case that is pretty straightforward to handle is the null and undefined case.
[00:02:40]
The readme said that nulls and undefines should coercively match each other. So if match is coercively equal to null and v is coercively equal to null, then we know we can go ahead and push that. I guess I should have done an else in here. We know we can go ahead and push that into the array.
[00:03:09]
Remember the match, I'm sorry, the null and undefined, in the case of coercive equality here. Remember that that's safe, that nulls and undefineds are coercively equal to each other and to nothing else. So we don't have to worry about any weird corner cases of negative zeros or anything like that in this particular case, all right.
[00:03:29]
So if we were to run, I'll pull just that one over into my run environment here, and we should see a few more of our tests now pass.
>> Kyle Simpson: And there is a couple down here that I think now that are now passing, okay. So progress. Now the next one we know is that we wanna allow strict matching with Booleans, meaning Booleans only match themselves, they don't allow any coercion at all.
[00:04:00]
So if the type of, and in this case we wanna specifically narrow ourselves to what's happening with match. If match is a Boolean then that signals to us that we need to strictly match with the v. So basically here if type of match equals a Boolean, and the type of v equals a Boolean, cuz if it doesn't then they shouldn't match at all.
[00:04:36]
If the type of match is Boolean and the type of v is also Boolean, we know we have both Booleans, but we definitely need them to actually be the same Boolean. So true needs to be true, and false needs to be false. So we could, and I'll explain a little bit later why we're doing this, but I'm gonna nest an if statement in here.
[00:04:56]
In the case of those of us knowing that both of those are Booleans, we still need to check to see if they are the same one. So we can say if match equals v. Now we don't need a triple equals here because we've already restricted the surface area to know that both of these are definitely Booleans.
[00:05:17]
Now I said earlier, you wanna avoid the double equals when there's a Boolean. That's generally true, but if both of these are Booleans, if you're trying to test against Booleans, then we know that double equals here is gonna do the same thing as triple equals. So either way, you want to use the double equals or a triple equals there.
[00:05:35]
Since the types are matched, they're gonna do exactly the same thing, okay. So if they are exactly the same value, then we go ahead and push into v, okay. Now the next two cases where some more of the complexity comes in. At first it seems easy and then we have to remember negative zero kind of gunks up all the works because of the way that it lies.
[00:06:00]
Okay, so first off we said if, and let me just point out, I forgot to point out, our object.is is already handling this part for us, so that clause is already handled. So now all we need is the strings coercing to numbers and numbers coercing to strings. But we need to remove those corner cases that we don't want to allow to occur.
[00:06:19]
So if we wanna allow strings, so a match could be a string as long as it isn't an empty string or a white space only string. So let's code that up. We have an else if (typeof match == "string" && match.trim() !) Is not equal to the empty string.
[00:06:45]
That handles both the empty string and any white space only strings. So we know matches a non white space, non empty string. And we wanna allow a coercion here but only if v is a number, so we need that clause. And type of v is a number. But this won't fully satisfy our condition, because you'll remember that this one only catches it in terms of the if else if clause logic.
[00:07:21]
This one only catches it, something like a negative zero, if both of them are negative zero. What if only one of them is negative zero? So a negative zero might end up in either the match or in the v position. And what do we know about the coercion that happens with a negative zero, is that when we're going from a number to a string, we lose the negative.
[00:07:47]
So we're gonna end up missing, if we pass in a negative zero to match against and we coerce it to a string, we're gonna end up missing it. So we want to remove from the set of our consideration any negative zeros, okay. So I'm gonna have to add in, let me just double check myself here, I'm gonna have to add in a check to make sure we're not dealing with a v of negative zero.
[00:08:20]
>> Kyle Simpson: Okay, and then what we wanna do then is allow the double equals coercion operation to tell us that these are coercively equal. So we've eliminated all the corner cases that we don't care about with that surrounding if statement, and now we just simply wanna allow the double equals to either put it in the array or not put it in the array.
[00:08:41]
Same as we did before.
>> Kyle Simpson: Okay, and then the final clause is the other way around, if match is a number and there's a several numbers that we don't want to allow in there. So we're gonna have to say if it's a number but it's not one of these numbers.
[00:08:56]
Then we want to allow that coercion from string to number. All right, so if typeof match == "Number", and then let's just refresh. What are the values that we didn't want to pay attention to there? They are NaN and the two infinities. So I'm gonna write this, actually I'll go ahead and break this onto different lines here, a little more readable for you.
[00:09:36]
All right, so we don't wanna consider any case where match is NaN,
>> Kyle Simpson: Or it's infinity or it's negative infinity, okay. So those three cases for sure are out. But we're gonna end up, and by the way, we want the clause in there, type of v has gotta be string.
[00:10:07]
So we've got a match of number, and we wanna allow it only if type of v is string. But we have the exact same problem here as we had in the previous one, which is that negative zero could come in and gunk up the works here. So we need one final statement in here that says,
[00:10:26]
>> Kyle Simpson: That matches not negative zero. So I'll add that one in.
>> Kyle Simpson: And then we don't want to, remember the string matching, remember these rules up here, the string matching? So we don't just have to know that type of v is a string, but we also have to know it's a non empty, non white space only string.
[00:10:51]
So we've got to add those clauses in as well. And there's a lot of refactoring obviously that you can do at some point, seeing all of the duplication here. I'm just trying to show you the logic of what are these corner cases that I want to be avoiding.
[00:11:03]
So we wanna say, v.trim is not equal to empty string. That gets us into a scenario where we can trust the double equals to make the coercive comparison.
>> Kyle Simpson: And if I didn’t make any other omissions, let me just double check.
>> Kyle Simpson: That should be it. So if I haven’t made any other mistakes or omissions, hopefully this should pass all our test cases now.
[00:11:43]
Just like the weather man, we'll cross our fingers and hope. All right, so I'll drop in the find all utility, and we will run it. And we get all trues, yay. All right, so just as a little bit of a kind of reality check here. This was a very contrived scenario where we said, we wanna implement our own subset, if you will, of coercive matching.
[00:12:18]
That's not likely something that you're gonna implement. But the purpose of this exercise was to illustrate the larger point which is coercion can be safe when you have eliminated the corner cases, and made it obvious that the corner cases are eliminated. So the style of coding that I was using here was intentional, which was to say this outer if statement is eliminating the corner cases.
[00:12:42]
And then inside it you can clearly see a double equals that we can trust that is safe because those corner cases have been eliminated, okay. So that's kind of an illustration of the larger point about trying to be more obvious with your types instead of always trying to be as clever as possible and have the most reduced code possible.
[00:13:02]
Sometimes a bit of duplication and a bit of explicitness and a bit of obviousness makes the code communicate a little bit more clearly, okay. So any questions about the exercise on coercive equality?
>> Speaker 3: Do you have been able to do the same thing with using a filter?
>> Kyle Simpson: So you're asking if we could have done-
[00:13:26]
>> Speaker 3: Instead of pushing things, could we start out with one, start out with the array and then filter?
>> Kyle Simpson: Yeah, so, could we have, what you're asking is, could we have implemented the matching logic of the findAll function instead of a utility that's standalone, cuz we have implemented it as what's called a predicate function.
[00:13:44]
Meaning it just returns true or false if something should be thrown in or not, absolutely, yep. So basically all of this stuff would be the substance of our predicate function, and rather than having a findAll utility, we'd just simply say values.filter then pass in our predicate function. So I would have been a totally valid way of doing it as well.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops