 Everybody, it's Brian, and in this episode we're gonna talk about the QMap class. This is a confusing class for many, many reasons, but basically the big confusion, it's a templated class, but it takes two different types. A key and a value. It forms what's called an associative array using a key value pair. Feel free to dive into the documentation. It does get a little murky the deeper you go. But at its core, it looks something like this. Instead of using a positional index like 0, 1, 2, you can use a key. And that can be any type. For example, we can use a string, let's call this key 57. Let's call this e22, notice these don't actually have to be in any specific order. Then this one could be called cat. It simply doesn't matter. As long as you follow the same type in this example of string, that key has to be unique. That key will point, or I should say associate, with a value that is also stored. That forms the associative array. These values don't have to be unique. And guess what? This can just add and add and add all you want. It's actually extremely flexible. Let's dive in and look at a really, really basic example here. So I'm gonna say QMap, and because this is templated, I have to give it what we're working with. I'm gonna say QString, comma, and we want a double for a value. So the key's always first, the value's always the last one. And I'm gonna call this lucky. So this is just simply our lucky numbers. And I'm gonna say lucky dot, let's go ahead and insert. And I need our key, which in this case, because our ID is awesome, it already knows the key is going to be a QString. And then we need a value, which is a double. So let's go ahead and give it a QString. And let's go ahead and give it a value, which in this case needs to be a double. So I'm gonna say 22.5. From here, we can do multiple things. I'm just gonna say QInfo, and let's just print out the entire map. And we can also say QInfo, my lucky number is. And let's go ahead and access that. So I'm gonna say lucky. And then we're going to get it by the key, not by the position. Remember, our key is this right here, the first one. Save, run, let's see what this looks like, just as a simple example. Sure enough, there's our QMap, we have our key value pair. And we can say, my lucky number is, and then use the key to get the value. Let's dive really deep into this class and see how it works. Okay, one of the major complexities about QMap is how do you work with QMap with things beyond simple data types, for example, doubles or QStrings or integers. We wanna work with a QObject. So I have this cat class all ready to go. This is a QObject, which of course has its own complexities, the biggest being we cannot copy this. We've talked about this in previous videos, QObjects cannot be copied. You can see, while this is not a super complex class, it's more complex than say an integer or a bool. It's got getters and setters and private variables and things of that nature. So, let's go ahead and look at how we would create a QMap. I don't wanna use simple data types because, well, it's too simple, and you won't really understand QMap and how it works. So instead, let's make a type def, this is short for type definition. And basically, we're going to shorten or make an alias of the type. The type we're gonna work with is a QMap, which is a template. And we need to give it a key value pair. So the key is going to be a QString. The value, though, is going to be an instance of this cat class. Now, because we cannot copy, we can't just, you guessed it, give it a normal instance, we have to give it a pointer. So this is where the complexity jumps in. You can either use just a raw pointer, and then you have to manage the memory, or you can use the standard library, smart pointers, or because this is a cute video, we're gonna use the Q shared pointer. This is also a templated class, and this is why this gets super confusing. So rather than retype this every single time we wanna work with this map, I'm just going to use a type def and make an alias. And we're gonna call this at map. Very, very simple. I don't like making type defs, but they do serve their purpose, and I believe this is one of those reasons. All right, so let's go ahead and make a function called getCats. And getCats is going to do exactly what you think it does. All right, notice what we're doing here. We are going to make a QMap, and we're going to return it. Now, this is the other thing that confuses people. QMap itself is not a QObject, so you can copy this all day long. But you cannot copy QObject, which we have in our values, which is why we are using a pointer. And we're using smart pointer, so we have automatic memory management. Okay, now that you've completely slammed all that into your brain, you can move forward. To say int, i equals zero. i is going to be less than five. Let's go ahead and increment this. Now what we need to do is create our value. So I'm going to say Q shared pointer. And again, you can use the standard library built-in smart pointers. If you want, I'm just showing you the cute way of doing it. And this is going to be a new hat. Now, notice right off the bat, we don't have a parent-child relationship, and that's why we're wrapping this inside of a Q shared pointer. Remember the parent-child relationship for QObjects, the parent would automatically destroy the children, blah, blah, blah. I did all that in a previous video. We also covered Q shared pointer in a previous video. If you skipped it, go back, watch it. But basically, when cat is no longer needed, Q shared pointer will automatically delete it. Makes memory management dead simple. Now, we're going to take that Q shared pointer and we're going to call set age, which is actually calling our cat, not the shared pointer. That's something that's really awesome about that. So now let's go ahead and set the age, and I'm going to use our Q random generator with the global instance, founded. We haven't really covered this class a whole lot, but basically what it's going to do is make a random number, not a cryptographically secure number, but a random number nonetheless between one and five. So we want our cat to be between the ages of one and five. Now let's go ahead and set the name. And I'm just going to say unknown, because these are just a bunch of stray unknown cats. I know, so sad. Now we need to take our value and insert it into the map. So I'm going to say cats dot. And this is where a lot of people type append and then they're shocked there's no append. This is an unordered list. So what we really need to do is say insert. And now we need a key, which is a Q string. So I'm going to say pet. And let's go ahead and expand this out. Let's say plus Q string number, which is going to take Q string and convert an integer, in this case, I into a Q string. So we're just adding those together. And then we're going to add our smart pointer in as the value. I know that seems a little confusing. So quick recap, that's plus a Q string representation of I is our key. So it's going to be like pet zero, pet one, pet two, pet three. As long as that key is unique, it should work. And then we have our Q shared pointer, which holds a pointer to the cat, which is our value. We're doing all this. So we have automatic memory management. So it seems super confusing at first, but once you get into it, it's actually pretty straightforward. Now let's go ahead and say cat map. This is where type def really shines because we don't have to retype all that. Let's call this cats equals get cats. And if we wanted to, we can just simply Q info this out, save run, and let's see what this looks like here. So there it is in all of its glory. You can see we have our cats are constructed and this is the big, ugly, nasty Q map in memory of what it looks like here. And you can see we have our Q shared pointers holding the pointers to the cat classes. All right, our last section really didn't do us any favors as far as displaying a Q map. Let's rerun that just so you can see what it looks like here. Horrible. I mean, you can't read this. So let's make a better function. Let's show how to actually display a Q map. There's multiple ways of doing it. I'm just going to show you one that's dead simple. And we're going to say cat map address of and then whatever we want. So the reason why we're going to use the address is so we're not making a copy of it. I want to show you directly what that thing is. From here, if you already know C++, this is extremely simple. So I'm going to say for each and I want the Q string key in the cats.keys. And this is just simply going to return a Q list of strings. So should say a Q list of what our data type R key is. Now that you have the key, you can simply get the data that's associated with it. So for example, I can say Q shared pointer cat and I want the cats.value. And notice it's wanting the key. This is what I mean by it's not a positional or an index base at array. It's a key value pair. We have to tell it what key associates with that value. You can spoiler alert, add in a default value. We're not going to do it for this example, but you could if you wanted to. So if you were going to say, hey, if that key's not found, I want to know what to do with it. Now from here, it's very, very simple. Just say Q info. Let's go ahead and output that key so we can see what that looks like. And then let's go ahead and output the name of the cat. Go ahead and put the age of the cat. And then let's actually see the pointer itself. So I'm going to say ptr.data, which is going to give us that pointer to the actual object the Q shared pointer is using. We grab this, scroll down, and then it just becomes extremely simple. And I'm going to comment this out just so it doesn't become super confusing and let's run it. Sure enough, it's just that simple. At zero, pet one, pet two, pet three, pet four, it doesn't necessarily have to be in order. These keys just have to be unique. There's the name, there's the age. We got our Q random number generator. And then here's the pointer to each and every individual cat value in there. All right, so far we have created cats and we've displayed cats. Let's look at modifying an item. So this is where people are going to get very confused very fast, but we're going to try and overly simplify this. So I'm going to say avoid modify cats. So what we're going to do is we're going to take our cat map. You want to make sure you're using the address of or a pointer to it. That way you're modifying the actual list, not a copy. And we want the strings. We need Q string, E. And when I say we want the string, really what we're looking for is that key in our case, the key is a Q string. So very important, you understand what we're doing. We're going to look this item up by its key and then modify it. So let's go ahead and say if cat, actually let's call this cats, like trip me up just a minute there. So let's call this cats.contains. And now we want to make sure we have the actual key in there. Then let's go ahead and say Q info, modifying and then whatever the actual key is. This is where people get very, very confused. So if you say cats. And you try at, there's simply no at there. It's just not there. And that's where people go, oh, I don't understand. You want to access it like this using the brackets and then give it the actual key. In this case, I'm going to say our key and then you can call it directly. So we're going to say set name. Notice what's happening here though. It's saying cats, whatever the key is dot and then we're accessing that variable directly. So we can say like set age, 99. I don't know if you caught it, but as soon as I said set age, this turned from a dot to an arrow. So the ID is smart enough to know that we're actually working with a pointer. We can just grab this to the magic of copying paste. And let's just do that again. So I'm going to say dot on the keyboard. And this is where people get confused. You see like clear, create data. These are all part of the Q share pointer, not the actual cat class. That's again, not a complexity with the Q map, but when you start working with pointers and smart pointers and everything, it's going to get super confusing. So we're going to do set name and notice it changed the arrow for me. And notice it changed the arrow for us. And I'm going to call this lucky. And then we're going to go ahead and call our display on cats. See, it's really not that super hard. So back down here, let's go ahead and say, modify cat and we want our cats map and we want to modify the key pet too. Let's go ahead and run this and see what this looks like. Okay, so pet two is unknown. We modify pet two and now pet two is fluffy with an age of 99. And we've modified that directly in our cat class. The major takeaway from this little segment here is that you're going to access it from a key, not an index position. Now that we can modify an item, let's look at how to add an item or inserting it. So I'm going to just say, please say void. Let's call this ad cat. Again, one of the common mistakes is that if you don't do an address of our pointer, you're actually making a copy of the Q map and then modifying the copy and then wondering why your original did not actually modify. Oh boy, that's really confusing. So you info, adding and inserting. This is a little bit confusing because what's the difference between adding and inserting? Well, there really isn't one. You're not going to say inserted at position because there's really no concept of a position here. So what we're going to do now is we're going to say Q shared pointer cat. And let's call this PTR. And we want this to be a new cat. Again, we're not going to use the concept of a parent child relationship because well, guess what? We want the Q share pointer to manage that memory for us. And then we can just simply say PTR dot set age. And let's make this like an insanely old cat, thousand years old. And then PTR dot set name. Let's call this test cat. Test cat got irradiated and it's now immortal and test cat will live on forever. Now there's a couple of different ways of doing this. So for example, you can say cats dot insert. And you notice that there's no position. This is where people get really confused. They see this cons iterator pose, but it doesn't really work the way you think it would. Remember, you have to have a key value pair. So we're going to insert this as test and then add the value. So that will definitely add it, but there's also another way you can modify it directly like this cats. And then let's go ahead and display. Now you may be wondering which is the correct way of doing this. Honestly, I almost always do this simply because it's very obvious what you're doing where if this already exists, notice we're not checking to see if it exists. It's just going to overwrite it. And that can get a little bit dangerous. Let's go ahead and demonstrate this. So I'm gonna say add cat cats. Say run, let's see what this looks like here. So sure enough, there is our test cat at the very end. It did add it. Now remember I said every key has to be unique. So let's go ahead and call add cats multiple times, but the major caveat being we're not checking to see if it already exists. So this is an underlying problem with our code. What just happened? Do you see it? Test cat got deconstructed, meaning what we did is we reused that key. And you can tell because right here, test cat has this memory address. You don't have to memorize the whole thing, but just look at these last letters, A C E 60. But now test cat is A D zero B zero. So this is a completely different instance. And the previous one got deconstructed simply because we reused the name. Okay, that's frustrating. Let's swap this out and go back to insert. You notice we still have the same problem here. Now, why do I prefer using insert? Because it's very obvious on the screen what I'm trying to do. And whenever I see this, I'm very conscious of the fact that I need to check to make sure that key actually exists. All right, so the last little segment at cat really showed us a problem. If we try to add this multiple times without checking for the existence of the cat, we're gonna have problems. So what we need to do is learn how to search for items. And there's multiple ways of doing this as you might suspect. So let me go ahead and grab this. We're gonna do the old copy and paste here. Let's call this find cat. Now I'm gonna show you a couple of different ways here. So first off, if you ever tried to find a cat in real life, you know, just how frustrating and funny it can be. There's an entire Facebook group out there to finding cats and it's just as messed up as you can possibly expect. So let's go ahead and say Q info. So in the spirit of that group, we're going to actually try to mess this up. We're gonna say exists. And then we're gonna say cats.contains. And we're just going to give it our key and just see if this actually exists. So let's go ahead and grab that girl down here. Let's do the old copy and paste save run. And let's see what this looks like. Uh-oh, I made a boo boo here, didn't I? I forgot to tell it what to look for here. Let's call test. Let's see if we can get an existence of test. Save run. Let's see what this looks like. So test cat exists. Let's modify that key to an uppercase T just to verify what happens. Notice how it is false. So this is actually case sensitive as terms of the key. That's one of the major takeaways here is if you're going to work with a Q string or a string as a key, make sure you watch your case sensitivity. All right, now that we know that exists, we can do different things with it. So for example, we can do something like auto, I did tell you we're going to screw this thing all up. You're probably going to see some hate mail in the comments down below. Cats.find. This is something I see people do all the time. And then they ask, well, what did find actually return? Well, it returned an iterator. So we're going to give it our key here. Now we need to determine if it's actually a valid value. This is the frustrating bit. So I'm going to say, if iter is not equal to cats.end, then I want to go ahead and say Q info. And let's do the iter dot key and the iter dot value. So let's play around with this and see what this looks like here. Let's say value stored in iter during its initializations never used, but if we did, it went away. That was just my idea trying to catch up. Save run, let's see what this looks like. All right, so there it is. Test Q shared pointer. So let's go ahead and change our key that we're looking for one more time. Do the uppercase t for test. We already know that will be false not found. So exist false. And then because we are testing, scroll up right here, we can avoid that altogether. So I generally don't work with fine just because it does get a little bit verbose and it gets a little buggy and error prone trying to figure it out. When I say buggy and error prone, usually the problem is with me, not the computer, but I just want you to be aware of that. Okay, so now let's go ahead and how would we get every single value? Save for each. And I want to do a Q shared pointer. Screwed that all up, didn't I? Let's say we're going to mess this up. So Q shared pointer, call this value. And we want the cats.values. So values is going to return a list of all the values with no key associated with it. So if you just want to go through every single value, you don't have to figure out what the key is. This is perfect for you right here. And then you can just simply do Q info, print that value out. Let's go ahead and run this, see what this looks like here. So exist false, it never did our iter and we can get all the values and find, oh, it's right there. But the major problem with this is of course it doesn't give us the key. Oh, that can be a bit frustrating, right? So let's just take this and change this. So we want Q string, which was our key data type. Let's call this key. And instead of values, we want keys. And this is going to do exactly what you think it would. It's going to go through all of the keys. And let's go ahead and say key, let's do the value. And this is where we have to say cats.value. And then we hand it a key. In this case, the key that is in our for loop right up here. And there is another thing we could do. We could do a constant or I should say a default return type as a const. I don't like to do this cause I like to make sure that I work with things that actually exist. But if you're working with ins and doubles and things like that, yes, this would be perfectly fine. But when you work with pointers, you want to make sure you actually have a pointer there. So I tend to avoid that. All right, so let's go ahead and do that. Save, run, and let's see what this looks like. Now remember we use the uppercase t for our key which didn't exist. So just iterating through or I should say looping through the keys, we can see the key value pairs and we can see the correct spelling of the key right there. Major takeaway from this is if you have no idea what you're looking for, you can just simply do a for loop or you can just go through a different way if you really wanted to. It makes it ridiculously simple to work with and it's nice and smooth. Let's look at how we would remove an item. So I'm going to just paste this and let's call this remove cat. We got our cat map and our key. Let's go ahead and check to make sure it's actually in there. So I'm going to say if not cats.contains our key, then we want to go ahead and basically tell the user, so sorry, try again. Say Q warning, in case you're wondering what Q warning is, it's part of Q debug, but it's just a different way of displaying a message. On the screen you're probably not going to notice a difference, but we're going to cover that in depth later on. In case you're really curious, it has to do with logging. We'd want different levels of notifications and logging. So we're just going to tell the user warning, the cat was not found, and then just hit the eject button and go right out of this thing. If we've gotten this far, well, it's actually very simple. So we're going to say cats.remove and then we just give it the key. And then let's go ahead and see what this looks like. I bet you were thinking it was super, super challenging. It's really not. So let's go ahead and remove our test and see what this does. All right, so bang, be constructed because we've removed that guy right here. And now our list is nice and neat. Doesn't have that test class in there at all. The major takeaway here is you work with pretty much everything through that key. It's very, I should say problematic or error prone. If you try working with the values directly, always use the key and then use the Q map built in functions to do any modifications you need to do. Okay, so if you're a C++ expert and you've been following along, you're probably having a miniature heart attack right now because we've been working with pointers. So we got all this stuff constructed but where is it being destroyed? Well, this is why we've been working with a smart pointer. In this case, Q shared pointer. Again, you can use the built in standard library but this is a Q tutorial. So I'm going to use the Q shared pointer. It's very, very simple because we're using automatic pointers as soon as we remove an item or clear that list. Let's go ahead and demonstrate that. All of that memory would be freed up automatically. You just haven't been seeing it in the background. So let's run this and demonstrate. Ta-da. As soon as we remove it from the map, Q shared pointer comes in and goes, oh, it's no longer needed. The reference counts reduced to zero and it is deconstructed. Okay, so to summarize an overview, this is an example of how to work with the Q map and we're using pointers, specifically smart pointers and then working with the key value pairs and then following that object's life cycle. I hope you enjoyed this video. You can find the source code out on github.com. If you need additional help, myself and thousands of other developers are hanging out in the Voidromes Facebook group. This is a large group with lots of developers and we talk about everything technology related not just the technology that you just watched. And if you want official training, I do develop courses out on udemy.com. This is official classroom style training. If you go out there and the course you're looking for is just simply not there, drop me a note. I'm either working on it or I will actually develop it. I will put a link down below for all three of those and as always, help me help you. Smash that like and subscribe button. The more popular these videos become, the more I'll create and publish out on YouTube. Thank you for watching.