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

The "Lookup 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 implements the lookup function, which returns an element based on a specified symbol. The toLowerCase method is used to make the lookup case-insensitive.


Transcript from the "Lookup Function" Lesson

>> I figured we start with the easy stuff first, the little bit lighter work, which is I've got an array. Remember, I just had that, I should have left it open. Remember, I have this JSON file. This is giving me an array of objects, and we know with an array, the positioning of elements that's numerically indexed sequentially, right?

Given a lowercase h, for example, how do I find and return just this object? That's what the lookup functions job is to do, is to find the element in the list and return it. So let's, for example, let me do a shorter word. I like this word, it's funny that you can spell words like this.

All of those are single letter elements. You can spell the word yucky, so let's imagine that that's what we had typed in and we somehow found those symbols, so we're just hard cutting that for now. And we're gonna test our little lookup function, we need to be able to find each one of those symbols, okay?

So let's write some code to do that. I'm gonna give myself some space here so it's easier to read. We have an array, it's not indexed on or property named on the symbol name. So we don't have that available to us to just look up and say single operation, give me the element of why we're gonna have to look for it.

So let's just do that work. Again remember that my suggestion is always to write the most naïve and straightforward implementation first, and then optimize, thinking of course about optimization as you go. But don't get wrapped up in optimizations too early, you do want to have a good reference implementation.

So I'm gonna do a for of loop here to go over, I'm sorry, not of elementSymbol. The variable that we have is called elements, so I'm gonna do for (let element of element), sorry. So each one of those is those object in that array, and I'm gonna say if (element.symbol), that's the property name in there, The a party takes care of sending this one to us in lowercase letters.

That part is already taken care of, so you don't need to worry about lowercasing the input. But you'll notice that the symbols here are not lowercase, so we do need to take care of to do a case insensitive comparison, there's a couple of different ways to do that.

You can use a regex, we're gonna just lowercase the symbol. So I'm gonna say .toLowerCase on that symbol. I'm gonna see if it's equal to the element symbol that was passed in. I always get people asking me why I use double equals instead of triple equals, and I've got a multi-hour answer to that in my other deep JavaScript foundations course here.

But the simple answer is, when I'm comparing two strings, the double equals works exactly the same as the triple equals, and I just don't wanna type the extra character. So since I know how the types work, I'm gonna use double equals here. But if you want to unpack that more, check out my other course.

Okay, so we're going to go through this whole list of elements. This is the most kind of naïve algorithm we can think of. In the worst case, we're gonna have to go through all of them. There's 118, so it's not like we have to go through millions of elements in the periodic table.

There's only 118, but in the worst case, we have to go through all of them for each symbol. So if we spelled out a word with five symbols, worst case, we'd have to go through about 570 something times. Okay, so let's talk about, so once we find the element, we know that it is matched.

All we need to do is return element, okay? And we could probably do an empty object return here. We're going to assume that lookup is never past anything that we don't already know is that, because all the inputs to the lookup function come from the check function. So it would not have given us a symbol that we don't know is there, which makes line 31 technically optional.

We know it's gonna find it somewhere. But if you wanted to make this slightly more robust, you could come up with some kind of default thing that returned with properties. A default thing, maybe that's helpful for error checking like return a default so that it's obvious that your thing didn't find, or that it returned something it didn't find.

But for now, we know that the only thing we're gonna pass to that lookup, so I'm just gonna save that. I'm gonna switch back over to my browser, and I'm gonna refresh, and it doesn't matter what I type here, because we hardcoded it to the word yucky. But let's just check to make sure that we are in fact pulling up all of those, and that in fact was showing up correctly.

It won't all fit on the screen at once, but there you go. So we know the app rendering part, and we know the lookup is working correctly. As a little side note, one thing that an algorithm should always be asking themselves, and I think this is true of really all engineering, but especially the algorithms, we have a lookup function that with one test worked correctly.

That does not guarantee us that it will work under all conditions. We can reason about, we can infer that the conditions where it would fail are not conditions that our function needs to support, which is why we left off that return statement or default. But that's not really a good test suite that I ran it with one hardcoded thing and it returned what I expected.

So we should ask ourselves at every time that we write some implementation, how much do I need to do to write a test suite for this? What is the minimum number of work that I need to do? And the reason I call this out, this question of testing algorithmic work, I've run into this problem many, many times in my career, and I think you will run into this problem.

If you haven't yet, you will eventually run into this problem, which is that you write an implementation of something. You write some algorithm for something, some code to do some task, and you want to test that it is doing the right thing. And the generation of a sufficient number of test cases is impossible to hardcode, right?

Here, there is only 118 symbols, so we could make a test case for each one of those. You could hardcode those out, or maybe programmatically generate those test cases. But if our data set was in the thousands or tens of thousands, there's no way you're gonna probably write a test for every one of those 10,000 different cases.

You don't necessarily need a test for all of them, but you need more than a couple to have a guarantee. That all the different variations of 1 and 2 letter and 1 symbol and 12 symbols, and is there a max? You wanna test all of those cases. And what you run into, that I have run into on many occasions, is that you run into this problem where the only way to generate the test cases is to use the algorithm that solves the problem.

In other words, you end up in this problem where you're basically testing does 1 equal 1? I run my code to generate all of my possible test cases, and then I run my code against those test cases and they all work, is that testing anything? Quick story, years ago, actually before I even started teaching here, so many years ago, I got one of what I thought was a dream job.

I got hired to work on the developer tools team at Mozilla for the Firefox browser, and this was before Firefox 4. My starting day was the second day of Firefox 4 being in existence. This was way, way back in the day. First external hire that Mozilla made to that team, and I thought this was gonna be awesome.

I'm gonna work on the developer tools and get to interact with developers and make better tools for them. For those of you that maybe have been around long enough, at that point, the only developer tools in the Firefox browser were made by a third-party, non-Mozilla employee. Actually, a couple of people made this, but they were not employees of Mozilla, and it was called Firebug.

And that was the only developer tools we had for Firefox for many, many years and Mozilla had finally said, we're gonna build in developer tools. So they'd already had a couple of internal employees working on the task for a little while. I was the first external hire to the team.

And to get my feet wet, they said, okay, we're gonna give you a couple of bugs to fix. And so the first bug that I ended up tackling as an employee there was related to the zoom. That when you had zoomed in and they were rendering the developer tools, it was the developer tools were rendering incorrectly.

And I think it was actually the overlay where you're inspecting a DOM element and it draws a little square around it. The square was drawn in the wrong spot if the page was zoomed in, so it's yeah, it's a fairly straightforward kind of a problem. And so I go in, I dig into the code and I figured out solving this bug was actually pretty straightforward, because there was this property exposed on the document object like zoom ratio or something.

And I just put in document.zoomratio as multiplying all of the calculations that the developer tools were doing to draw stuff by the zoom ratio, and boom, it worked. Now all of my squares are being drawn in all the right places, so it was a couple of lines of code, not that big a deal.

But to put in this code, I also have to put in a test for it. They have very strict guidelines, no code without tests. Right, they've got all kinds of guidelines there, no performance regressions, all kinds of stuff, but put in your code, you've gotta put in a test.

I'm like, all right, how am I going to test this? And it turns out that there was no independent way to verify what the zoom ratio of the browser was from JavaScript other than this property. So my code is multiplying everything by zoom ratio, and then my test cases are dividing everything by zoom ratio.

So I was quite literally writing test to see if 1 equals 1, and I submitted those tests and they were like, yeah, that's fine. I'm like no, it's not fine. I had code comments like this is dumb, these are my tests, but this is not sufficient. This is not actually testing anything.

We're just testing if in one place I multiply by it and in another place I divide by it. This is pointless, right? The end result of that story is that it turned out that to write actual tests for that required us to create this whole mechanism by which we could take screenshots of the page at the zoom ratio and compare pixels.

It was bonkers, I wrote hundreds of lines of code for the test that took two lines of code in the app. And it taught me this lesson, very painfully, that thinking about how you're gonna test stuff as you're writing it, and what am I gonna test this against then?

How am I going to get an independent implementation of this to test against? It can be challenging, and it is never more challenging in my experience than when we deal with algorithms. When you start dealing with large datasets and algorithms, we've got people here that are into machine learning, you know this, right?

The only code that exists to produce the data set in question is the code that's solving it, so how do I use that to generate my test? All right, so little side note on testing, just be aware of those kinds of problems. But we feel pretty good for now that lookup is working, so let's now shift our attention to a little bit more of the heavy lifting, which is the check function.

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