Practical Problem Solving with Algorithms

Refactoring the Highlight Function

Kyle Simpson

Kyle Simpson

You Don't Know JS
Practical Problem Solving with Algorithms

Check out a free preview of the full Practical Problem Solving with Algorithms course

The "Refactoring the Highlight Function" Lesson is part of the full, Practical Problem Solving with Algorithms course featured in this preview video. Here's what you'd learn in this lesson:

Kyle refactors the highlight function to utilize the diagonal data structure. A Map variable is used to create references to store major and minor diagonal indices for each element. The final solution for the Chessboard Diagonals problem can be found on the option-4 branch.


Transcript from the "Refactoring the Highlight Function" Lesson

>> Let's move down to the highlight function. And I'm gonna temporarily comment this out, that reminds us that we need to come back and fix that. Let's come down here. We know that we were previously getting the title row index and title column index, and we were pulling that out from the dataset, right?

So we could still store it into the dataset and then here, we could recompute the major index and the minor index. Are you with me? I could copy these computations down here. And then I would know based on the tile that was clicked, I would know what major diagonal index and minor diagonal index, that particular clicked tile belongs to.

You just copy the math and do it again. But even that is redoing computational work, it's not that much computational work, it's trivial math, but it is redoing work that we already did. So I'm gonna go back up here, and I'm gonna further rethink things. Why am I computing the index and then pushing it in, only to then have to later we can put the index?

Earlier, somebody mentioned that if we wanna store more rich information with a DOM element, we could use a map. And that's what I wanna do. I don't wanna store the index that you're in and then have to go retrieve that index. I actually want to associate with each tile element, a reference to the diagonal that it belongs to, the two diagonals that it belongs to.

So here's what we're gonna do. We're gonna create ourselves a variable, we'll call this tileDiagonals. And we're gonna make capital M map, an instance of capital M map. This is different from objects, because remember, this is going to allow us to use anything as the key. And in this case, we're gonna use DOM elements as the key.

Are you with me? Let me hear thumbs up, thumbs down. Have I lost you yet? We're doing okay, all right? So, at this moment, I want to insert an entry into that tile diagonals map that includes references to the two diagonals that it belongs to. Are you with me?

And I'm gonna key it by the tile element. So in other words, I'm gonna do something like this. I'm gonna say tileDiagonals., I think it's add. I'm sorry, set. tileDiagonals.set, that's the method on maps. tileDiagonals.set, what's the index? It's the tile element. And what is the value? Well, why don't we just collect the two references to the two diagonals?

So how are we gonna get those two diagonals? Well, here's what I'm gonna do. We actually move this down. I'm just gonna combine this, instead of saving it separately as indexes, I'm just gonna get a reference to the major and minor diagonals. So now I have my major diagonal and my minor diagonal.

>> And those are the two things that I want to put their references in right here. Does everybody follow how we did this? We took our tile element and used it as the key to store two element array, a tuple, that includes the major diagonal and the minor diagonal in it.

The major diagonal and the minor diagonal are references, so this is just merely more indexing. These are references to the actual arrays that we created up here that we stuffed the tile element reference into. So it's kind of making like a double linkage like, the diagonal knows about the element and the element knows about the diagonal.

You see that. And we're using the map as a way to create that, you might think of it as a third party association between the two. We have a DOM element and we have a tuple array, and we wanna create a relationship between the two, and we store that relationship in our tile diagonals map.

Thank you map data structure, that's a nice, good solution for us. It's gonna be nice and efficient. Now that we come back to highlight, this is where the fun stuff starts. What do I need to do with a tile element to get its major and minor diagonal which I might call the things that I need to highlight?

So I'll call this toHighlight. That's gonna be my little tuple array. I'm gonna call tileDiagonals.get, and I'm gonna give it the tile element. Everybody with me? There are two elements in there, so I'm going to say, for (let diag of toHighlight). And then for let el of, I'll spell this out instead of diag.

El.classlist.add("highlighted"). That should look familiar. That's the same way we've been highlighting the tiles all along, so that's nothing new. The only thing new is this outer for loop is now going over our new tuple from our tuple index into our data structure. Otherwise, this already looks exactly the same kind of loop that we were doing before.

Just go over all the el references and add highlighted to all of them. Are you ready for the fun part? All of this we get to delete, I just love. That's all gone. Because this is it, this for loop is literally all we need to do to highlight that diagonals for a tile element.

So take just a moment, we're not quite done, but take just a moment. Make sure you understand how we were able to remove all that other stuff and how this all it's doing, it solves the problem. We are storing in the map a reference from a tile element to the two diagonals that it participates in.

And when we are clicking on a tile element, we retrieve that tuple to know the major and minor diagonals that it participates in. And when we're simply looping over any elements that are in either of those two diagonals and highlighting. We don't need to keep track of any indexes or row, none of that, we don't have to keep track of any of that.

We've already stored a data structure that gave us a flat reference to the elements that we will need to care about. But there's one last little detail which is that I commented this code out, so that we would remember that we needed to come back to it. How do we know which elements that we need to unhighlight?

You remember that up until this point, we've only had this basically dumb algorithm which was just try to unhighlight all of them. But now spot this. This tuple represents the stuff that we highlighted before, and that's information that we're trying to basically recompute or ignore, why don't we just save that?

In fact, instead of declaring this as a variable, I'm gonna rename this to highlighted. I'm gonna declare that as one of the variables that I have in my module scope, so that I can remember it over time. And here's the fun part. This code right here, this nested for loop, I'm gonna copy it, I'm gonna paste it right here, unindent it one level, and change add to remove.

That's it, that's our entire solution. How many people think that we drastically simplified the way that we solve this problem? And indeed, haven't we improved the performance of this? Because we are now only looking at the tiles that we care about, and we don't even need to recompute which tiles those are because we save that information as we construct the board.

To me, this is one of the clearest of the various examples that I could give you. It's one of the clearest examples of how powerful it is when you have a data structure that is aligned with your problem Instead of trying to align your problem to your data structure.

Sometimes that means finding a data structure that somebody else has already written. Go get the NPM package, install it, use it, boom, you're done. Sometimes, you have to get a little bit creative and construct our own list of diagonals arrays. You shouldn't be scared of that. Was this painful code?

I feel like, by this point, most of you should feel pretty confident that all we did was construct some arrays, there wasn't any sophisticated algorithmic thinking here. The only light bulb moment that I had to have was turning the problem by 45 degrees, and that was it, to make sure that we didn't make a mistake somewhere which does happen.

Let me go do our regression test and make sure we didn't make a mistake. And it works. It's important for you to have fingers on keyboard with this stuff. If I had simply told you this, it wouldn't really click, so having the fingers on keyboard is an important characteristic.

But I will say that the bigger takeaway from this exercise is not the code that we wrote, it's not the code that we deleted. It's the discipline and the willingness to ask questions about our problem, to back track and ask different questions, and to attempt things, in this case, at a 45 degree angle.

I don't know if any of you have ever looked weirdly at your code before, I've done that before. This is a literal manifestation of the, what is it doing? Now I see what it's doing.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now