So I woke up today and thought to myself that there would be nothing I would like more than to spend inordinate amounts of time solving puzzles and trying to manage my ego being constantly bruised because I have the brain power of a precocious amoeba and the narcissistic tendencies of Thor. If I wasn’t so stubborn, I would get a mailroom job somewhere and bootlick my way up the corporate ladder instead of putting in hours to become a(n) (ab)useable commodity in the modern working world.

Not a great plan.

I wonder if contract killers ever feel this way? Did they take a career aptitude test, get “mailman”, and just say “Fuck it, let me get on the darkweb”? Being a postal worker must almost definitely be a front for a more highly lucrative and seedy career, kind of like how rows of carwashes and nail-salons in close proximity are definitely legitimate businesses. ;)

I’d like a car-wash and a bag of met-I mean…Skittles

Problem 1: Multitap Keypad Entry on an Old Phone

(https://www.codewars.com/kata/multi-tap-keypad-text-entry-on-an-old-mobile-phone/train/javascript)

Instructions: Prior to having fancy iPhones, teenagers would wear out their thumbs sending SMS messages on candybar-shaped feature phones with 3x4 numeric keypads.

— — — — — — — — — — — -

| | | ABC | | DEF |

| 1 | | 2 | | 3 |

— — — — — — — — — — — -

— — — — — — — — — — — -

| GHI | | JKL | | MNO |

| 4 | | 5 | | 6 |

— — — — — — — — — — — -

— — — — — — — — — — — -

|PQRS | | TUV | | WXYZ|

| 7 | | 8 | | 9 |

— — — — — — — — — — — -

— — — — — — — — — — — -

| | |space| | |

| * | | 0 | | # |

— — — — — — — — — — — -

Prior to the development of T9 (predictive text entry) systems, the method to type words was called “multi-tap” and involved pressing a button repeatedly to cycle through the possible values.

For example, to type a letter “R” you would press the 7 key three times (as the screen display for the current character cycles through P->Q->R->S->7). A character is “locked in” once the user presses a different key or pauses for a short period of time (thus, no extra button presses are required beyond what is needed for each letter individually). The zero key handles spaces, with one press of the key producing a space and two presses producing a zero.

In order to send the message “WHERE DO U WANT 2 MEET L8R” a teen would have to actually do 47 button presses. No wonder they abbreviated.

For this assignment, write a module that can calculate the amount of button presses required for any phrase. Punctuation can be ignored for this exercise. Likewise, you can assume the phone doesn’t distinguish between upper/lowercase characters (but you should allow your module to accept input in either for convenience).

Hint: While it wouldn’t take too long to hard code the amount of keypresses for all 26 letters by hand, try to avoid doing so! (Imagine you work at a phone manufacturer who might be testing out different keyboard layouts, and you want to be able to test new ones rapidly.)

— — — — — — — — -

What we start out with…

a blast from the past for starters. Remember flip phones? Now-a-days if you’re still carrying one of these around it’s referred to as a burner, which means you either sell drugs or have a side-piece somewhere…which also means you lead a pretty interesting life and make bad choices. Good for you.

What we start out with (code wise)

function presses(phrase) {// To do…}

Adrian’s Solving Steps:

  • Create a variable starting at 0 to be our answer.
let answer = 0;
  • Create an object with
  • one being the key for values (in an array) ‘A’,’D’,’G’,’J’,’M’,’P’,’T’,’W’,’*’,’ ‘,’#’, ‘1’
  • two being the key for values in an array ‘B’,’E’,’H’,’K’,’N’,’Q’,’U’,’X’,’0'
  • three being the key for values in an array ‘C’,’F’,’I’,’L’,’O’,’R’,’V’,’Y’
  • four being the key for values in an array ‘S’,’Z’,’2',’3',’4',’5',’6',’8'
  • five being the key for values in an array ‘7’, ‘9’
let object = {one: [‘A’,’D’,’G’,’J’,’M’,’P’,’T’,’W’,’*’,’ ‘,’#’, ‘1’],two: [‘B’,’E’,’H’,’K’,’N’,’Q’,’U’,’X’,’0'],three: [‘C’,’F’,’I’,’L’,’O’,’R’,’V’,’Y’],four: [‘S’,’Z’,’2',’3',’4',’5',’6',’8'],five: [‘7’,’9']};
  • Now we turn the phrase argument (a string) we are passed into a version of itself being made entirely uppercase via the string method .toUpperCase( ) and then we turn THAT into an array with .split(‘’)
phrase = phrase.toUpperCase().split(‘’);
  • Now, since phrase is now an array of upperCased values, we use the .forEach( ) method on it with callback function arguments of current (for the current value) and index (for the index of the current value in the array phrase)
phrase.forEach((curr, index)=>{//good shit coming soon})
  • Then I originally had a series of for-loops where we would iterate over the length of the various arrays in our object that we created.
  • The gist is that if curr happens to equal any of the values in a given array, our answer variable would augment by a given integer. I wound up making 5 of them, in direct violation of D.R.Y. principles
for(let i = 0; i <= object.one.length — 1; i++){   if(curr == object.one[i]){   console.log(‘match’);   answer += 1;   console.log(answer);   }}for(let i = 0; i <= object.two.length — 1; i++){   if(curr == object.two[i]){   console.log(‘match 2’);   answer += 2;   console.log(answer);   }}for(let i = 0; i <= object.three.length — 1; i++){   if(curr == object.three[i]){   console.log(‘match 3’);   answer += 3;   console.log(answer);   }}for(let i = 0; i <= object.four.length — 1; i++){   if(curr == object.four[i]){   console.log(‘match 4’);   answer += 4;   console.log(answer);   }}for(let i = 0; i <= object.five.length — 1; i++){   if(curr == object.five[i]){   console.log(‘match 5’);   answer += 5;   console.log(answer);   }}

lol…do not do this.

  • So I decided to make a function that would basically do all of that and avoid such a heavy repetition of code.
function doubleTime(array, number, curr) {   for(let c = 0; c <= array.length; c++){      if(curr == array[c]){         answer += number;      }   }}

What’s happening here is basically a set up where you can pass in the name of the array, the number you want to increment by when there is a match, and curr

For whatever reason, I couldn’t figure out how to programmatically get the names of the arrays, so I wound up doing the following as my final solution.

function presses(phrase) {let answer = 0;let object = {   one: [‘A’,’D’,’G’,’J’,’M’,’P’,’T’,’W’,’*’,’ ‘,’#’, ‘1’],   two: [‘B’,’E’,’H’,’K’,’N’,’Q’,’U’,’X’,’0'],   three: [‘C’,’F’,’I’,’L’,’O’,’R’,’V’,’Y’],   four: [‘S’,’Z’,’2',’3',’4',’5',’6',’8'],   five: [‘7’,’9']};function doubleTime(array, number, curr) {   for(let c = 0; c <= array.length; c++){      if(curr == array[c]){         answer += number;      }   }}   phrase.forEach((curr, index)=>{      console.log(curr);      doubleTime(object.one,1, curr);      doubleTime(object.two,2,curr);      doubleTime(object.three, 3, curr);      doubleTime(object.four, 4, curr);      doubleTime(object.five, 5, curr);   });return answer;}

I wanted to programatically call doubleTime I really did.

Best Practices:

function presses(phrase) {var chunks = [‘1’, ‘ABC2’, ‘DEF3’, ‘GHI4’, ‘JKL5’, ‘MNO6’, ‘PQRS7’, ‘TUV8’, ‘WXYZ9’, ‘ 0’],phrase = phrase.toUpperCase().split(‘’),total = 0;   phrase.forEach(function(l) {      var key = chunks.filter(function(c) {      return c.indexOf(l) > -1;      })[0];      total += key.indexOf(l) + 1;    });   return total;}
  • They made three variables
  • chunks an array with groupings of the values based on how they are on the phone
  • phrase, just as we did, gets made into an uppercase version and then turned into an array.
  • total — zero.
  • They use a .forEach( ) method on the phrase array

Inside of it, we establish a new variable key as being an array made by a filter function run on chunks, and we pass the argument of c as the element being processed in the array at that moment. Then we build our filter array based on whether or not the value of l is present in the newest array c. We do this by evaluating whether the index is more than negative -1. If it is, then it exists, because of zero-indexing.

*It is also worth nothing that we pass an array with 0 in it as the value to use as this when executing the callback.

[0]

*My working theory, because this confuses me too, is that it if the return c.indexOf(l) > -1 evaluates as true then key equals the portion of the chunk that caused it to evaluate as true…so it could be one of the elements in chunks.

**tl;dr; key becomes an array out of a portion of chunks that we certify as having a value that is passed in from phrase.forEach(function(l) {}) argument.

then we make the total equal whatever it is plus the index of whatever l represented in the .forEach method callback within the context of key + 1….because zero indexing.

This one is kinda confusing to me too. It’s super clever, but that random [0] chilling at the end of the .filter( ) method instance kinda seems a bit arbitrary to me.

Problem 2: Directions Reduction

(https://www.codewars.com/kata/directions-reduction/train/javascript)

Instructions:

Once upon a time, on a way through the old wild west,…

… a man was given directions to go from one point to another. The directions were “NORTH”, “SOUTH”, “WEST”, “EAST”. Clearly “NORTH” and “SOUTH” are opposite, “WEST” and “EAST” too. Going to one direction and coming back the opposite direction is a needless effort. Since this is the wild west, with dreadfull weather and not much water, it’s important to save yourself some energy, otherwise you might die of thirst!

How I crossed the desert the smart way.

The directions given to the man are, for example, the following:

[“NORTH”, “SOUTH”, “SOUTH”, “EAST”, “WEST”, “NORTH”, “WEST”].

or

{ “NORTH”, “SOUTH”, “SOUTH”, “EAST”, “WEST”, “NORTH”, “WEST” };

or (haskell)

[North, South, South, East, West, North, West]

You can immediatly see that going “NORTH” and then “SOUTH” is not reasonable, better stay to the same place! So the task is to give to the man a simplified version of the plan. A better plan in this case is simply:

[“WEST”]

or

{ “WEST” }

or (haskell)

[West]

or (rust)

[WEST];

Other examples:

In [“NORTH”, “SOUTH”, “EAST”, “WEST”], the direction “NORTH” + “SOUTH” is going north and coming back right away. What a waste of time! Better to do nothing.

The path becomes [“EAST”, “WEST”], now “EAST” and “WEST” annihilate each other, therefore, the final result is [] (nil in Clojure).

In [“NORTH”, “EAST”, “WEST”, “SOUTH”, “WEST”, “WEST”], “NORTH” and “SOUTH” are not directly opposite but they become directly opposite after the reduction of “EAST” and “WEST” so the whole path is reducible to [“WEST”, “WEST”].

Task

Write a function dirReduc which will take an array of strings and returns an array of strings with the needless directions removed (W<->E or S<->N side by side).

The Haskell version takes a list of directions with data Direction = North | East | West | South. The Clojure version returns nil when the path is reduced to nothing. The Rust version takes a slice of enum Direction {NORTH, SOUTH, EAST, WEST}.

Examples

dirReduc([“NORTH”, “SOUTH”, “SOUTH”, “EAST”, “WEST”, “NORTH”, “WEST”]) => [“WEST”]

dirReduc([“NORTH”, “SOUTH”, “SOUTH”, “EAST”, “WEST”, “NORTH”]) => []

See more examples in “Example Tests”

Note

Not all paths can be made simpler. The path [“NORTH”, “WEST”, “SOUTH”, “EAST”] is not reducible. “NORTH” and “WEST”, “WEST” and “SOUTH”, “SOUTH” and “EAST” are not directly opposite of each other and can’t become such. Hence the result path is itself : [“NORTH”, “WEST”, “SOUTH”, “EAST”].

Adrian Failed….hard:

I gave up, ngl.

function dirReduc(arr){let answer = [];function reducer(arr, answer){console.log(‘reducer running’);console.log(arr);for(let i = 0; i <= arr.length -1; i++){   if(arr[i] == “NORTH” && arr[i+1] ==”SOUTH”){   arr[i] = ‘’;   arr[i +1] = ‘’;   }if(arr[i] ==”EAST” && arr[i+1] ==”WEST”){   arr[i] = ‘’;   arr[i +1] = ‘’;   }}for(let z = 0; z <= arr.length-1; z++){   if(arr[z] == “SOUTH” && arr[z+1] == “NORTH”){      arr[z] = ‘’;      arr[z+1] = ‘’;   }   if(arr[z] ==”WEST” && arr[z+1] ==”EAST”){      arr[z] = ‘’;      arr[z +1] = ‘’;   }}arr = arr.filter(element => element.length > 1);console.log(‘filtered’);console.log(arr);   for(let x = 0; x <= arr.length-1; x++){   if(arr[x] == ‘’){   reducer(arr);   } else {   answer = arr;  return answer;   }  } }}

It was kind of a western spaghetti mess as you can see.

…kinda like this movie.

but here’s the solution from people that could crack it:

function dirReduc(plan) {var opposite = { ‘NORTH’: ‘SOUTH’, ‘EAST’: ‘WEST’, ‘SOUTH’: ‘NORTH’, ‘WEST’: ‘EAST’};return plan.reduce(function(dirs, dir){console.log(dirs);console.log(dir);console.log(‘jump’);   if (dirs[dirs.length — 1] === opposite[dir]) dirs.pop();     else dirs.push(dir); return dirs;   }, []);}
  • I added the console.logs so we could investigate

Holy crap. Let’s all bow down to the .reduce( ) function again. It seems like EACH TIME I make one of these fucking articles I’m singing the praises of the reduce method…I really need to learn how to recognize when I should use it more quickly.

  • We make an object called opposite with keys and values being the opposites of eachother in terms of cardinal directions.
  • We return plan, our argument for the problem which is an array of a bunch of directions.
  • We use .reduce( ) and pass in dirs (which has a symbiotic relationship with the empty array at the end) as the accumulator or the reflection of the new array we are making with .reduce( ) then we pass in dir as the current value being dealt with in our callback function that is endemic to the array-method .reduce( ).
  • We then use an if-statement and say:
  • If dirs **initially an empty array** has a value at the index of it’s current length -1 that equals the value in the object opposite using next currentValue (aka dir) as a key…then it removes the last element of the array.
  • The value of opposite[dir] never even gets put in and then we go onto the next part of the plan array.
  • Yeah, that was pretty baller.

Problem 3: Your order please

…I couldn’t resist.

(https://www.codewars.com/kata/your-order-please/train/javascript)

Instructions: Your task is to sort a given string. Each word in the string will contain a single number. This number is the position the word should have in the result.

Note: Numbers can be from 1 to 9. So 1 will be the first word (not 0).

If the input string is empty, return an empty string. The words in the input String will only contain valid consecutive numbers.

Examples

“is2 Thi1s T4est 3a” → “Thi1s is2 3a T4est”

“4of Fo1r pe6ople g3ood th5e the2” → “Fo1r the2 g3ood 4of th5e pe6ople”

“” → “”

Ok…this one doesn’t seem too awful.

Adrian Solved it:

function order(words){let array = [];words = words.split(‘ ‘);for(let i = 0; i <= words.length; i ++){   for(let c = 0; c <= words.length -1 ; c++){   let target = i + 1;   let block = words[c];      if(block.includes(target)) {         array.push(words[c]);      }   }}return array.join(‘ ‘);}
  • I made an empty array named…array (so creative).
  • I make our function’s argument words into an array.
  • We use two for-loops:
  • The first one iterates over the length of our new array words.
  • And for each single value in words, we will call it value-prime, with a counter of i
  • We have yet another for-loop that iterates over the length of words again and has a counter as c
  • We create a variable called target equalling i plus 1.
  • We create a variable called block equalling whatever word the nested for-loop is focusing on
  • Then we make an if-statement, where if the block variable we made includes target(which will always be a number)….then we will push whatever value is held by words[c].
  • We return the array as a string via the use of .join( )

It’s probably not the best solution, as we’ll confirm in a bit…but I did it, and that feels good.

Best Practices:

function order(words){return words.split(‘ ‘).sort(function(a, b){return a.match(/\d/) — b.match(/\d/);}).join(‘ ‘);}

This one is sleek, small, sexy and lacks any sort of nested for-loop mumbo jumbo that would cause, potentially, some confusion in dealing with tweaking the fine details.

  • We turn our argument words into an array, and also make the elements of the array be picked based on whether they have an empty space between them.
  • Then on our array version of words we use an array method .sort( ).

A word on .sort( ) this is a pretty nifty array method. By default, it will sort things in an alphabetical or numerical order that is ascending 1 to 10, A — Z, that sort of thing. However, we are trying to sort our array elements based on the number that is placed at random inside of them.

To meet our needs, we pass a callback function as an argument to the .sort( ) method **an optional choice**, and in our callback function we have two arguments: a & b which will represent the first and second elements for comparison, respectively.

Inside of our callback function we have a command to return a — b. I will get to the use of the .match( ) method in a bit.

When we use .sort( ) and placeholders for values like a & b there are three ways to know how the sorting is going to work:

With regards to a and b and the arithmetic that takes place inside the callback for .sort( ):

  • if the returns a negative value, the value in a will be ordered before b. (ASCENDING)
  • if it returns 0, the ordering of a and b won’t change. (NOTHING)
  • if it returns a positive value, the value in b will be ordered before a. (DESCENDING)

So if you look back at the block for the .sort( ) callback you’ll see that we have ab. But to avoid dealing with alphabetical sorting, we append both of those arguments with .match( ), a string method, which takes a RegEx expression to match only numbers. THAT’S THE BIG TRICK TO THIS.

Then we simply turn the array that words currently is, back to a string via .join( ) with a ‘ ‘ **a space** as the argument for that method.

Et voila! We can finally order some gibberish from a potential dyslexic person that we’re totally going to need to build this exact code for.

…so practical ;)

A Note:

So writing CoDEVIANT and being a kinda, sorta pretend misanthropic developer-person is all kinds of awesome, and I can definitely feel my JavaScript and problem solving capabilities growing. Also, I get a nice dose of humble pie whenever I look at the “Best Practices” answers, and humility is good for a person. Truth be told however, and maybe it’s due to opera rehearsals on top of it, but my side-projects are feeling a bit left out in the cold lately. That said, I’m going to try to do just ONE problem a day, and strive to really understand the shit out of it and take the new lesson or a lesson I’ve seen a few times before and can’t seem to incorporate into my thought process upon repeated exposure into my actual work work-flow. However, if there are similar problems that I run into while refactoring my portfolio pieces and making things for clients, I will definitely share and discuss how I made it work...if I can. 😅 😅 😅 😅

“Best Practices” solutions for things like these….may never happen…because…well I’m not there yet. But I hope this series is helping someone out there, besides just myself, to look at the process of breaking down something that looks like dog-shit gibberish and really grasping it ((Or at least going through the motions of it. 😅))

Peace.

is a web developer, opera singer, actor, and lover of cats. (adrian-rosales.tech)