Intro to Elixir | Lesson 14: Drills and Tips - Part 3

Elixir
Transcript

English (Auto-generated)

knowing the series on elixir today, we're going to do more drills and tips and we're gonna solve this particular problem which is writing a piece of code that finds the most recurring letter in a given string. Um I really like this question because regardless, regardless of the programming language you're solving it and it's it's a kind of question that kind of stretches and and requires putting together multiple bits and pieces of of of code I should say to to come up with a solution. So this is why I really like it and then you know, I think it makes a good interview question as well. So um let's go ahead and get started here. So solution again here, just to reiterate, I don't necessarily always give you the best solution or the most optimal solution or the most performance solution. Uh It really depends on the context in the situation in some cases. Um I try here to just help us kind of the most straightforward way to solve this problem in what we have learned so far. So I'll go here and I'll find a module um and then let's call it string helpers here and then here I can do something like that most recurring letter and then let's say it takes in a string and then this is the part here where we will solve it. So we could right here, string dot most recurring letter. Uh and then we just so we could test it, you can give it something like hello world. Um and then we just put some some random stuff here and then see what what comes up. Um Now in real life if you're solving this kind of problem, the first thing I would do is to ask for more details, more questions and things. Right? So I'm gonna make some assumptions in this particular one because the question doesn't imply it. But you know if if you if you get this question in an interview or you know in real life for some reason I would start by asking some questions like do we count spaces for example, do we count special characters and so on? Um in this part because it says letter so I'm not sure you know from just from from the text here what it implies. So but you know what let's let's go ahead and just remove remove all spaces. Um So what we can do here is we can take the string and I'm gonna use the pipe operator here because I know I'm gonna call many functions on this particular string. Um and then we're gonna just keep um we're gonna keep basically applying different functions and techniques until we come up with with the answer. So if you recall here I'll go to my ex. So the string library has a replace function. Okay, replaced so this one here takes in three arguments if you do a bit of research here so we could do something like this Hello World. So we give it the string first. We give it the string we want to replace. This can be a regular expression by the way. But let's just go ahead and and and just replace the space with empty string. In real life. I probably use a regular expression. I'm just keeping it simple right here. Regular expression because I could do other things and I can do more flexible things such as removing like non removing numbers, removing non letter characters and so on. But for now to keep things simple again remember here that with the pipe operator, this will be the first argument which is exactly what we want to remove this whole award, which is how hard coded. And this will basically take this string and then we replace the space with nothing with no string. So which means here will basically eliminate the spaces. A very important thing to note here is that notice that we have a strength. So it's always important to look what is the output of a particular function call because you want to when you want to call the next function, you want to make sure that the first input to the next function is the output of the previous function. So in this case the output of my function is a string. Just a string that does not include spaces in this particular case. Now here is we could think of what approaches we can take to solve this particular problem. All we did right now is we basically plans the string that that we got. Um So if I run my code, I'll just go run it for, you know, which is a good practice by the way, even if you present a small amount of code, it's just good to get into the habit of running. So if there's any like sometimes could be a typo or something, you could solve it early on. Okay, so notice here, I already made it actually a mistake. There shouldn't be string. It should be string helpful helpers. So I'll go with all these and I'll replace them with string helpers. And notice in both cases the spaces have been removed from those strings. Again, I'll remove these special characters for now. Just to make things simple and I'll even remove numbers. Um All right. So what do we do next? Well, generally speaking when you want to deal with letters of a string, it's a good idea to turn it into something like a list or some kind of a data structure that you could work with better than working with with strength. So if you recall, how do we turn a strength into a list of characters part of the string libraries? I'll hit top for you. Would you happen to remember it? It's actually the split method. So a split and then we take something like that, the world and then I don't think we need actually we do need to pass it. Not like an empty string here because if I don't pass it? An empty string? What I get is I get splitting by spaces and we already eliminated spaces. So um this is why this particular one would not work. So what happens here is um I replaced this um are replaced basically instead of instead of so instead of just putting space here, which is give us just a whole string, you put an empty string and then this will give us um splitting without using any spaces. Okay, so let's go ahead and do this and then split. And then we want to split. Remember here when I'm doing things in I X I do have to explicitly pass in the first argument, but what I'm doing is the pipe operator um the first the the output, the first argument is the output of the first function. The function before it, I should say so in this case it is a string that we have there. I notice here, I'm getting these empty strings in here, I'm actually not quite sure why I'm getting these. Let me try this again here. I think this it seems like I do get this kind of um um empty strings from the beginning, let me try here using a regular expression. I seem like I still get the same result. I'll investigate later while we're doing this. Um But you know when again actually just for the sake of it, I can probably find a better solution later. But let's say here I want to write a function that eliminates empty strengths. So what do you think? What library should I look at first of all, look what kind of data structure we have as a result of the split. That is right. This is a list. So we want to apply something to the list. So the first thing that you should think about is um basically um just go here. Mm hmm. So I go here and I type in on electoral. So as I mentioned to you before when you're dealing with lists or maps, the first library I would go to check things in would be the same library in this case I happen to know the the the the method which is reject. But again, you know, you could simply do something like reject for example or remove. Um and you may actually get some hints here, eliminate again, you could you could search within here or you could simply searching google but I just happen to know that the reject method what it does. You could reject a particular element or I shouldn't say a particle element, a number of elements in a list based on some criteria that you give it in function. So I could simply do something like this. I can actually show it to you here where I could do in um that reject. Okay in this case here I could say well I'll use the long form first I say well reject it if the number if the X. Or maybe I'll use s for string if the string is empty. And this function basically will reject all the strings here that are empty. So remember I could use the short format here which is I could do a percent. And then I say the first argument is empty and this will give me the exact same result. So I'll pick up here the short format. Mhm. So it's a bit more concise. And as I mentioned I would like you guys too be comfortable using this syntax so you're gonna see it a lot. Right? I know like again it was a little turkey when I first saw it but as you see it more and more I think it will become second nature to you. So what we did here, we took a string. We replace spaces with no strings were removed, spaces we split the string so this just put it through here. This returns a list and then we took the list. Um and then we rejected all the ones that have empty strings. All the um elements of the list that are empty strings. So we still have a list in this case awesome. So you know maybe I'll do one more thing here again. This is where I would ask questions if I was in an interview but not just how we have uppercase letters and lowercase letters. So probably what I would do before we even replace spaces for after. It doesn't matter in this particular case but we would do um string the damn case Okay string that down case. So in this case here string the down case and then we get all the way to do this. Now I just want to show you a little trick here. Now this will if I copy this module we really find this model if I try to um Okay that reject maybe it was not defined properly. Let me try this again. Mhm awesome. So string helpers. So what happens here is um I have now a list that contains all lower case letters, no spaces and then each letter of the swing that we have we have it as a separate element. The thing I wanted to show you here if you use I owe that person in this particular case. Um It will work because um the the list this you know um elixir treats um strings um you know as as a list of characters. So you have if you remember when we do something like this list it gives me true. Remember if we define strings with single quote these strings are defined as um as characters um as a as a list of characters. So this is why in this particular case this actually worked. But if let's say I try to do something like this I owe that puts and I try let's say to display a map I owe that puts will actually fail. So the one I wanted to show you here is I owe that inspect which is the most popular one which will display work well with most data structures and most data types. So this is why I'll switch all these here. Do I owe that inspect? This is a little nice tip for you as your debugging your application especially when you get bigger or larger amount of code, awesome. So now I have here also a list that contains all the characters. So the list looks something like this. This is where things get interesting. Now what I wanna do here, I want to go through each letter of this list and then I want to construct some way of saying well h record ones the record once. L record ones but then when I hit this, L. I want to say well by now L record twice because L. L. Right and then all once w once or twice and so on until you get to the end of the strength. So first of all, what do you think is a good data structure for us to use if we want to take in if we want to keep track of how many occurrences happened for each letter. I would say probably a map would be a great one to do. And this is usually my go to we could use we could use um a cured list. Remember a cured list? The keys always have to be adam's. So we have to figure out a way to convert those two atoms, which isn't a problem in this particular case because they're just simple characters and if we're only dealing with characters then using them as as, as atoms would be, would be a good practical thing to do. That said, I'd say, you know, generally speaking, I find math to be a lot more useful and a lot easier to work with. So what we want to do here, we want to take this and we want to construct a map to actually keep track of how many um how many occurrences each letter has. Now this is a pattern here where not necessarily unique to elixir, but you're gonna see it a lot in a lecture. That's why I want to spend a little bit of time digging into it. You're probably familiar with it in other languages in case you're not. Um I would, you know, this would be a good time to do it in our situation. What we have, we have a list that have items and what I want to do here. I want to construct. I'm not, okay, so I have a data structure and I want to construct another data structure based on the original one. I would say when you see patterns like this, not only, you know a list to a map, it could be a map to a list, it could be a map to keyword list, it could be, you know, whatever it is. This is usually a good kind of you could think of it as as an indication that you may want to make use of the reduce method that is available for us in the library. I would say almost every programming language I work with have a version of other reduced method. So this is why I'll spend a little bit of time to actually explain how reduce works in case you haven't seen it before. So the reduced function in our case it's in and that reduced, it's not the best the best friend here. So dot reduce takes in some kind of a data structure here, innumerable. So in this case, let's say in our case it will be the basically this list and then we give it some kind of a starting point and usually the starting point doesn't have to be, but usually it's what you want to return at the very end. So in this case we want to return a map. So maybe we'll start with an empty map. Okay, Maybe it will start with an empty map here as kind of think of it as our starting point. Then the third argument would be a function and this is where things get interesting the function think of it as a way that you basically run things recursive, lee where the first element is the element from this list. And then the second argument is what's usually called the accumulated output. So in this case here, the map that we keep building, we keep passing it again and again and then we put our code here. What's interesting is when you call this function, you go to the next element in there and then you basically pass, you passed the output here back again as the second argument and the element will go from the first, let's say the first element here, let's say I have character H. And then I have character E. So basically, second time you run it runs it will go through E. But then the accumulation will be whatever. The first one which is a function from H has um has resulted. So in this case maybe I'll try to help you a little bit more on this. So let's say our function here initially it will have the argument age in our case of Hello World and it will have an empty map. Okay, so what we need to do is we need this function to return a map that looks like this, it will have the letter H as the key and then the variable. Sorry, the value will be one because it only occurred once. What happens is you call the same function again. But now what happens is The first argument would be we're going from H 2E. So it will be but the second argument will be existing right here. So this will be the second argument for this and then what you want is the output of this function should be okay. H one and then E one mm. Hmm. And then you pass this as the third argument for a function with this will be L and so on. So it's a way to recursive, li call the function again and again, until you get the full um the full map which will have the number of occurrences for each letter. Then we'll figure out how to pick the maximum at the very end. But let's actually start to write this function right here. And by the way, this is all um kind of documented for you in reduce and there's different versions of reduce. There's it produce there is that I'm gonna use is just a standard to reduce. You could read up on the different ones, which um basically here's an example. So reduced. So for example, in this example, what we're doing, we're taking a list and then we're expecting to get an interview at the end. So we pass zero and then this reduced will basically give me the sum of all the elements in this particular list. So what I'll do here is I'll do in um that reduce and then I'll take a function, I'll put here as a character and then here I'll put I'll call it, you can call it accumulator. That's kind of the general um name of the variable, but you can call it whatever you want. And then here this is where it gets interesting. What we need to do here is we need to take this map. So initially we have an empty map, but eventually we'll have something like H one. So what we need to do, we need to write a code that does something like this where it says, well if H exists in there then just add, you know, construct, remember, we cannot just mutate it. This is where I think you will, you know, if you're coming from a language that does think in in in a mutable data structure, you'd be inclined to say, well, you know, uh the accumulated of characters plus equals one. This would be a very simple way to do it. But unfortunately, because again, we would be mutating the map in here. This isn't really available for us in this particular case. So what we have to do, we have to construct a new map that takes into account either in stan she ating the occurrence of the letter to one, which means we haven't seen it before, so make it one or if it exists before, then um Then add 1 to it. Um the message that comes to mind here, when you want to construct a new map is basically what we're doing is we're either we're basically entering, we're doing put, so I'll show you here the method that you know, maybe if you did a better research, you will find it yourself, which is the put method. So if you have a map, what I can do. So let's see here. I have a map that have letter H. I did have one. And if I say put H. As to What would happen is each will become too, if I say put ES2, it will keep the aged electoral ad basically es tu. So which means here, if if the key exists, it will overwrite it with the new value because remember keys cannot occur twice in maps, they can increase your list but not in maps. So this is why in this case it will overwrite the value each with two. But remember when I say override, we're getting a new a new hash and a new map in this case. Uh if it doesn't exist then it will basically add the new key value pair for you. So that's why this would be our friend here. The map output. So what I'll do here is mapped output. It's the accumulated result which is to start will be an empty Oh sorry, I forgot to put the first one here, which is the empty map, which will be the first entry point to our um our reduced function and then we take characters. So we're looping through each character of the list at the time and we have the accumulated result to take the accumulated results and put character here, but this is where it gets interesting here. So we basically either put um the accumulated result at that particular character, but if it doesn't exist then what do we need? We say we can see or zero. So if it doesn't exist, remember it, we will we will basically get get a null and then add one to it. What do I mean by this? Well so my map here, let me actually use the same function names. So let's say here, I say h here's one. If I say not H. one, just th um if I say something like accumulator of age is one, so I say over zero, I'll still get one. But if I say accumulator of, let's say we're the next letter here, E it's nell if I say or zero then it will get zero. So I'm either insane. She ating the value to zero, which means it doesn't exist or to to to fetch the value if it already exists and in both cases we want to add one to do it. Okay, so we want to add one to it and that's basically what we're doing. So let's go ahead and test our code so far. So Alright, so notice here that d. one e 1 H one, L. 302 R. One and then W. One. So the code works perfectly like we expected it to do so to recap here, reduce is a really nice method. I would highly encourage you to get very comfortable using reduced, I would say, you know, it's hard to, you know, like write a decent feature in elixir without using the reduced function. It's it's really powerful, it gives you a lot of power and especially in in a functional language, you're always constructing new data structures based on old ones when you know, when you want to manipulate data or format data or something. So that's why this comes in very handy because we start with the data structure, we end up with another data structure. So we start Dirty Deuce, we give it an empty map and then it basically reiterates through every character of this list, remember this, we still have a list of characters at the end of this method here and then we have an accumulated results which starts with an empty map. But then with every cycle we take the new character and then we say if it exists already implemented one, otherwise start with zero and add one to it, which will basically make it one. And this way here we end up with a map in this particular case, not just we have a map. So this is why here, I'm gonna write a little comment for you here. The strict turns the map, which is very important because now we have to know how to take this map and get the most recurring letter. So we have to figure out how to get the most recurring letter in this particular case it's the letter out awesome. So in this case map is also an innumerable, which means we could also check the imam library if there is a handy method that we can use to actually find the most um in this case the highest recurring letter which is by the value. And you know, with a bit of search, you will realize that there is a nice handy method in here called max by what this max by does is you actually give it some kind of innumerable and then you could find uh the maximum but it's called the maximum because you could actually do whatever you want, it will find the maximum in this case by its length. Um Now it may not be very visible from this example, but when you're dealing with maps and this will be the case in most in um library, you're not just gonna get notice how this function here is getting just a single X. Because when you're dealing with the list, you're dealing just with one element at a time. But when you're dealing with the map, you have pairs. And if that's the case then you will get those pears as a tuple. So in this case I'll show you how we could use the same method which is the max by but use it with the map and in this case, what I'll do here, I'll do, you know, not max by remember here. The first argument would be the map returned from this previous function. Now in this case here I'll get the key and value but I get the key and value of it to the function as a couple. So all I have to say, well get me the maximum by the value. It's really as simple as simple as this. So um let's see what we get right here. So what we get is another couple that actually gets me the maximum of each um each pair inside there. I noticed I get a um warning here. Just his variable K. Is unused because all I told the max by function here is finding the maximum by value. So I didn't really use the K. So what I would do in a situation like this, I would just put an underscore before the K. Which tells the reader, hey, we're not gonna use the K. It is the first argument, so we need to have it there but we're not going to be using it to put an underscore before that awesome. So what we have right here is we have a couple that has the basically the um the letter and the number of times that it occurred. So what I can do here, I could put something like result. So here I'll just put for you, something like this returns a tuple, it's formatted a little bit nicer here. So I get the tuple here again, this is here where you know, so I love about this example, like we had to work with strings, lists, maps and two poles all in a single example um which I find really really nice. We had to use anonymous functions here, we had to use the library, we had to use the pipe operator. So this example almost puts everything we've learned together into a single example. So here's what I can do, I can return here, the most recurring letter is and then I can use string interpolation here and in this case the result is a tuple. So how do I get the first element in the tuple um you can either say, you know, if I have a couple, like I'll just copy exactly this one here. I call it the result. I can either do result zero, which is what I will sorry, um Sorry, in this case it will not work that way. So we will have to use the one I wanted to show you which is the 11, which you could actually use as a pipe operator. So element here is you basically pass in the tuple which is the result and then you tell you what elements you want to access in this case zero or one. So you can access an element in the tuple using this alarm when you see a public function like this. Uh It's the same as doing kernel dot which I might have shown you earlier. So this is what you might call a public function available in elixir, which is part of this kernel library that has built in. Let's go ahead and use this. So I'll use the most recurrent letter is it's the first one, so result zero. Yes. And it occurred then we do another string interpolation, but we get the second one here. Okay, that's that's it. One more time. Okay. Most recurring letter is elegant, occurred three times the most recurring letter is A and this one right here, you can check out for yourself later. It occurred five times most recurring letter. And this one is a and it occurred um 10 times. Um let me try one more. Maybe let me put some more efs maybe or something. Most recurrent letter is f. And it occurred 12 times awesome. So this might have been a bit of a heavier kind of an exercise um than what we've seen before. And um you know what I would encourage you to do um try to just stop this cast um and try to do this exercise all over again without looking. Uh And just, you know, use google to search again, you'll you'll be using google to search for the right functions and methods and stuff. Um In in in a lecture all the time. So this is a good time to practice that. Um as I mentioned here to me, this exercise is a really good measuring sticks of, of knowing all these things that we've we've we've learned so far. So I would say, you know, go ahead and uh, and um, try to do it yourself, come back to this cast if you're stuck. And um, yeah, like, uh, I think once, once you feel you're at least understand all the things that we've covered today, you know, you could move on to future lessons. Thank you for joining and I'll see you next time.
200 Views 1 Likes 3 Comments

We will solve the puzzle: find the most recurring letter in a string using Elixir.

Comment
Leave a comment (supports markdown format)
@amy 1 year ago

Learning to use the reduce function is super important but it’s definitely not the simplest way to go about solving this! There is actually a built-in Enum function, Enum.frequencies that can make this simpler for us!

This is the simpler solution that I came up with!

 def most_recurring_letter(string) do
     string
     |> String.downcase()
     |> String.replace(" ", "")
     |> String.split("", trim: true)
     |> Enum.frequencies()
     |> Enum.max_by(fn {_, x} -> x end)
   end
@amy 1 year ago

You can add optional “options” to the String.split() function! To trim those ", just add trim:true to your split() call, so it looks like:

 String.split("hello world", "", trim: true)
@jarius 2 years ago

I’d never come across Enum.reject before. Glad to know it. Might want to use String.split(“”, trim: true), and save some cycles, or drop the first string replace, and reject on &1 in [“”, “ “].

“Hello World” |> String.split(“”) |> Enum.reject(&(&1 in [“”, “ “] or “Hello World” |> String.replace(“ “, “”) |> String.split(“”, trim: true)