CoDEVIANT #5 (3/25/19)
Misery loves company…so why is coding a solo thing mostly? Because it’s agony, mental anguish, the gnashing of teeth, and the slurping of cold brews. The Christmas Tree topper on the whole monstrosity? The condescending attitude held by those who just figured out something that you only began to struggle with. Good thing I, and by extension you (I guess), are all masochists!
Problem 1: Best travel
(https://www.codewars.com/kata/best-travel/train/javascript)
John and Mary want to travel between a few towns A, B, C … Mary has on a sheet of paper a list of distances between these towns.
ls = [50, 55, 57, 58, 60].
John is tired of driving and he says to Mary that he doesn’t want to drive more than t = 174 miles and he will visit only 3 towns.
Which distances, hence which towns, they will choose so that the sum of the distances is the biggest possible to please Mary and John- ?
Example:
With list ls and 3 towns to visit they can make a choice between: [50,55,57],[50,55,58],[50,55,60],[50,57,58],[50,57,60],[50,58,60],[55,57,58],[55,57,60],[55,58,60],[57,58,60].
The sums of distances are then: 162, 163, 165, 165, 167, 168, 170, 172, 173, 175.
The biggest possible sum taking a limit of 174 into account is then 173 and the distances of the 3 corresponding towns is [55, 58, 60].
The function chooseBestSum (or choose_best_sum or … depending on the language) will take as parameters t(maximum sum of distances, integer >= 0), k (number of towns to visit, k >= 1) and ls (list of distances, all distances are positive or null integers and this list has at least one element). The function returns the “best” sum ie the biggest possible sum of k distances less than or equal to the given limit t, if that sum exists, or otherwise nil, null, None, Nothing, depending on the language. With C++, C, Rust, Swift, Go, Kotlin return -1.
Examples:
ts = [50, 55, 56, 57, 58] choose_best_sum(163, 3, ts) -> 163
xs = [50] choose_best_sum(163, 3, xs) -> nil (or null or … or -1 (C++, C, Rust, Swift, Go)
ys = [91, 74, 73, 85, 73, 81, 87]choose_best_sum(230, 3, ys) -> 228
First of all…Mary & John need to get their shit figured out. Also, why can’t Mary drive? What kind of patriarchal bullshit is this?
At any rate, I can tell right away. That I’ll need to have 3 elements from the array of distances and get unique combinations for each. I’m going to be honest…I don’t think I can do this one. I think I’d have to be able to
- Get mini 3-length arrays that are unique combinations of the full list of distances
- We need to get sums of distances from these mini arrays
- The distance we can accept must be smaller than whatever value is represented by the argument t
I’m going to punk out and try to learn how to solve this shit by looking at the Best Practices answer:
and….
function chooseBestSum(t, k, ls) { var biggestCount = 0; var recurseTowns = function(townsSoFar, lastIndex) { townsSoFar = townsSoFar || []; //base case if (townsSoFar.length === k) { var sumDistance = townsSoFar.reduce((a,b)=>a+b); if (sumDistance <= t && sumDistance > biggestCount) { biggestCount = sumDistance; } return; //EJECT }//recursive case for (var i = lastIndex + 1 || 0; i < ls.length; i++) { recurseTowns(townsSoFar.concat(ls[i]), i); }}recurseTowns();return biggestCount || null;}
….
Yeah…if someone had put a gun to my head and told me to solve this. You’d have to be planning a funeral for ya boi. So let me state my demands incase some nerds take my hostage and put such tasks to me.
- Dress me up in blue; crip-style.
- Put my H&K 9mm USP with a tac-light & a silencer in my hand just like in MGS2.
- I want gold chains with lots of bling around me.
- Studded grills too.
- Make sure my fingers rigor mortis up in a crip-sign.
Okay…so let’s try to analyze w̵h̵a̵t̵ ̵t̵h̵e̵ ̵f̵u̵c̵k̵ ̵i̵s̵ ̵g̵o̵i̵n̵g̵ ̵o̵n̵ ̵h̵e̵r̵e̵ this solution.
so a new function is created inside of the function that we are trying to create.
This new function is called recurseTowns. It is created using a less commonly used way of creating functions; declaring a variable and making it equal to a function.
var action = function(arguments) {//stuff that we do}this is pretty uncommon
--------------------------------function action(arguments){//stuff that we do}
you’ll typically see this a lot more often
Inside the function recurseTowns, you’ll see that it takes two arguments:
- townsSoFar
- lastIndex
You’ll also notice that no where prior to the calling of this function are there any variables with names townsSoFar or lastIndex. It’s things like this that make me want to curl up in a ball sometimes or throw a VHS tape across a room, or chuck a glass at a tree.
But fear not…there must be a way…somehow…
Right away inside of the code-block for the function recurseTowns, you’ll notice that townsSoFar is set to equal itself OR (which is signified by the use of an Or-Operator || , it will simple equal an empty array.
townsSoFar = townsSoFar || [];//townsSoFar equals itself and if itself doesn’t exist, then make it an empty array.
In the line following the defining of townsSoFar we see an if-statement that is decided whether to execute a block of code if the length of townsSoFar happens to equal the argument k (which itself is the number equalling how many towns our couple has agreed to visit).
We create a variable named sumDistance which is equal to the adding of the elements in the array townsSoFar, which is accomplished with the .reduce( ) array method.
- IF sumDistance is less than-or-equal to the amount of miles the couple agreed to travel, t, AND sumDistance is larger than biggestCount
- then biggestCount will equal the value of sumDistance.
This if-statement is important because we are saying in the premise of our problem that the couple has agreed to a maximum of t miles. However, given the itinerary of selecting only k amount of places to visit, there is a chance that they may exceed the amount of miles they have agreed to travel OR it may turn out that there are only certain towns they can visit that get them just a little bit shy of their maximum.
Should the number of towns they choose to visit exceed the amount of miles that have been agreed upon then we simply return…which triggers recurseTowns( ) being called again, but the lastIndex value is still in memory which results in instead of us getting
ls[0], ls[1], ls[2] as values we’d get something like ls[0], ls[1], ls[3]….
Disclaimer: I may be a bit hazy on how exactly an array is dropped after it gets to k length…if someone wouldn’t mind helping me understand it better if I don’t get it right, I’d actually really appreciate it 😅
So what then if townsSoFar’s length is NOT equal to k (the amount of places we have agreed to visit)???
Well, good question…
We roll into the for-loop…remember how we had an if statement? Well, it is tacitly implied that the conditions are not met to grant the computer access to the code-block that is germane to that if statement, then we roll on to the next chunk of code. For beginners and semi-noobs like me, I tend to prefer having extra brackets and code-words to help me out.
function chooseBestSum(t, k, ls) { var biggestCount = 0; var recurseTowns = function(townsSoFar, lastIndex) { townsSoFar = townsSoFar || []; //base case if (townsSoFar.length === k) { var sumDistance = townsSoFar.reduce((a,b)=>a+b); if (sumDistance <= t && sumDistance > biggestCount) { biggestCount = sumDistance; } return; //EJECT } else { //recursive case for (var i = lastIndex + 1 || 0; i < ls.length; i++) { recurseTowns(townsSoFar.concat(ls[i]), i); } }}recurseTowns();return biggestCount || null;}
Having a few more brackets and an else might have made it more clear, but these edits in no way change the functionality of the code.
Anyways, to the for loop! So we get a nice little comment above it that says //recursive case. This means that we are going to be playing with a concept called recursion. I’ll explain it a bit after I layout what’s happening inside the for-loop.
So we say that var i is going to equal lastIndex + 1…if this is the first time the function has gotten to the block of code the computer’s going to be like, “ay mang, I ain’t got no lastIndex” but the for loop is going to be like “aight, loc, don’t trip, I’mma hold you down, just use 0 instead since you don’t have it” via the OR-Operator. Then we say that our for-loop’s code is going to be run as long as var i is less than the length of ls. Also, each time we run this, i will increase by 1.
So, the first time we run through this, i will be zero. We then call the function recurseTowns and pass in townsSoFar but with the value of whatever location the array representing our distances between towns (at index i [currently zero]) as the first argument, and then i itself as the second argument.
//What it calls for → recurseTowns(townsSoFar , lastIndex)//what it looks like at this point ↓recurseTowns(townsSoFar.concat(ls[i]), i);[] -> [‘666’] , 0//whatever value lives at ls[i] will be shoved into townsSoFar
So then we loop through the function recurseTowns all over again. Let’s assume that we have not reached the number of towns we have agreed to visit and find ourselves, inexorably, back at our for loop.
THIS TIME it will be different.
In our for loop, i will not equal 0 because we came prepared with a value standing in for the lastIndex argument of the function…it happened to be 0, but now we will add 1 to it, making it 1 *this part here I DO understand 😉 * Then we end up running recurseTowns again, but with different values.
the code// recurseTowns(townsSoFar.concat(ls[i]), i);the first time// recurseTowns(townsSoFar.concat(ls[0]), 0); // lets assume ls[0] equals 666.this time// recurseTowns(townsSoFar.concat(ls[1]), 1); //let’s assume ls[1] equals 40.
This means that the variable townsSoFar now looks like the following = [666, 40]
We will continue to do all of this until the length of townsSoFar is equal to k which will trigger that code in the if statement that I covered earlier….at the conclusion of that if statement’s code, we return; Then below the initial calling of recurseTowns( ) we see
return biggestCount || null; // return biggestCount or return null;
And that is how we’ll know what the largest distance the couple can travel while visiting a predetermine amount of towns while also equalling or staying under the mileage limit they have given themselves. If they can’t pull it off, then null is returned.
..yeah, I know.
A word on recursion…
So basically…think about the Matrix Trilogy. Neo was the 7th “One”.
Each time the function being run by the Matrix completed, some value pertaining to an if-statement was not satisfied so another block of code was run that called the main function again in the hopes of a certain requirement being satisfied so that something being guarded by that requirement could run. That’s basically it in a nutshell. It can be very tricky to explain, but that’s the most easy way I can explain it.
In our problem k would be the number of Neo (in the movies it was 7). So once we would get to the if statement and townsSoFar’s length was also 7, then we would be able to have the DBZ style fight between Neo & Smith.
Which would give us either
- biggestCount (or Sati creating a new world, with Oracle & Architect saying stuff.
OR
- null (with Smith destroying everything).
So this one was a ball-buster……by all rights I should stop because my head hurts…but we are masochists after all.
Problem 2: Extract the Domain Name from the URL
(https://www.codewars.com/kata/514a024011ea4fb54200004b/train/javascript)
Write a function that when given a URL as a string, parses out just the domain name and returns it as a string. For example:
domainName(“http://github.com/carbonfive/raygun") == “github”domainName(“http://www.zombie-bites.com") == “zombie-bites”domainName(“https://www.cnet.com") == “cnet”
We want our function to pass these tests:
Test.assertEquals(domainName(“http://google.com"), “google”);Test.assertEquals(domainName(“http://google.co.jp"), “google”);Test.assertEquals(domainName(“www.xakep.ru"), “xakep”);Test.assertEquals(domainName(“https://youtube.com"), “youtube”);
How did Adrian solve it?:
function domainName(url){//We need to strip away http:// or https:////We need to strip away www.//then if there is anything that comes after a “ . “ we need to get rid of all of that and the period too.console.log(url); switch(url.includes(‘https://')) { case true: url = url.replace(‘https://',''); break; default: url = url.replace(‘http://',''); } switch(url.includes(‘www.')){ case true: url = url.replace(‘www.',''); break; default: break; }//make into an array, find where the ‘.’ is and delete it and everything past it. return url.split(‘.’)[0]}
So what were my steps:
- Cut away https:// or http://
- Cut away www.
- If there is anything after “ . “ we want to delete that shit.
I use a switch statement to evaluate whether or not the url argument includes the text string “https://“. We determine this with the .includes( ) string statement.
var string = “pizza”;string.includes(‘c’); // returns ‘false’string.includes(‘z’); // returns ‘true’
If the url, in our case, includes “https://“ we get ‘true’ which matches up with a case in our switch-statement. In that case we make url equal itself but with ‘https://' being replaced by nothing. We achieve this with a string-method called .replace( )
var string = “Cat Hate”;string.replace(“Hate”, “Love”) // we get string equalling “Cat Love”.
the second variable in the method .replace( ) takes the place of the first.
Now if the url DOES NOT have ‘https://' in it, then we have our default case equal ‘http://' which we replace with nothingness too.
Then we use another switch statement to see if url includes ‘www.’ if it does, we replace it with nothingness…
If ‘www.’ is not present in url, we do nothing.
switch(url.includes(‘www.')){ case true: url = url.replace(‘www.',''); break; default: break;}
Then with what we have left for url, we have to deal with everything that’s going to come after a .com, .org, .gov…there are so many possibilities it’s baffling…so instead of creating an array of every possible combination of suffixes that could be appended to a domain name, I decided to use the string-method of .split( ) to turn our url argument, that we have modified at this point, into an array.
With the .split( ) method, however, we can chose where we want the split to occur for the creation of our array.
If I had the word pizza as a string, and I wanted to split that string into an array that has elements separated by the occurrence of the letter “p”, we would do it like this:
var word = ‘pizza’;word = word.split(‘p’);console.log(word); // [‘’,’izza’]
So I’m going to do this on our url string argument thing…but split based on a period -> “.”
var url = google.comurl.split(‘.’) // [‘google’, ‘com’]; url[0] url[1]So url[0] would be ‘google’and url[1] would be ‘com’So let’s just cut out the middle man/extra steps and return url[0].return url.split(‘.’)[0]
and bam…we get ‘google’, ‘amazon’, ‘pornhub’…whatever it is I (ahem, you) we were looking at.
How did the smart people do it:
function domainName(url){ url = url.replace(“https://”, ‘’); url = url.replace(“http://”, ‘’); url = url.replace(“www.”, ‘’); return url.split(‘.’)[0];};
Okay, so honestly…not that much different. The main difference is that they didn’t waste time with needless switch statements. Other than that, the same string and array methods were used as was the idea to return the result of splitting our url argument.
Cool. :)