Day 2/30 Days of CodeWars: JavaScript Edition

nats_tech_notes

Nat's Tech Notes

Posted on June 1, 2022

Day 2/30 Days of CodeWars: JavaScript Edition

CodeWars challenges solved
1) Grasshopper - Personalized Message (8 kyu)
2) Parse nice int from char problem (8 kyu)
3) Correct the mistakes of the character recognition software (8 kyu)


Kata 1: Grasshopper - Personalized Message

Instructions

Create a function that gives a personalized greeting. This function takes two parameters: name and owner.

Use conditionals to return the proper message:

  • name equals owner => 'Hello boss'
  • otherwise => 'Hello guest'

Breaking down the problem

Parameters

As mentioned in the instructions above, the function expects two parameters: name and owner.

Based on the test cases, both are expected to be (valid) strings, though in actuality they might not be.

Returns
The function should return one value: a string.

What string should be returned depends on whether or not the name and owner match (see instructions above).

Examples
The following test cases are provided by CodeWars.

('Daniel', 'Daniel'), 'Hello boss'
('Greg', 'Daniel'), 'Hello guest'
Enter fullscreen mode Exit fullscreen mode

The first two values are the arguments that will be passed to the function, followed by the message that should be returned.


This coding challenge is fairly similar to the third kata solved yesterday with some minor differences. So instead of reiterating through the same solutions mentioned in that kata, I'll be providing some variations to those solutions as well as diving deeper into error handling.


Solution 1: Basic implementations without error handling

The following implementations assume that the arguments passed to the function are valid strings (like in the test cases provided by CodeWars).

Variant 1: using if/else statements
function greet (name, owner) {
  if (name === owner) {
    return "Hello boss";
  } else {
    return "Hello guest";
  }
}
Enter fullscreen mode Exit fullscreen mode

This is the easiest and probably most common implementation.


Variant 2: using an if statement only
function greet (name, owner) {
  if (name === owner) return "Hello boss";
  return "Hello guest";
}
Enter fullscreen mode Exit fullscreen mode

This is a similar approach to variant 1, with 2 main differences:

1) The if statement is written on 1 line and the {} after the statement's condition have been omitted. This is valid code for one-line conditional statements, though it is usually recommended to add the {}. It is also important to note that (most) JavaScript linters will add the {} when formatting code if they have been omitted.

2) There is no else statement. It has been replaced by another return outside of the if statement. This works because the function can only execute 1 return so if it does not execute the code inside the if statement, it will return the string specified outside of the if statement.


Variant 3: using a conditional ternary operator
function greet (name, owner) {
  return name === owner ? "Hello boss" : "Hello guest";
}
Enter fullscreen mode Exit fullscreen mode

Variant 4: using template literal syntax
function greet (name, owner) {
  return `Hello ${name === owner ? 'boss' : 'guest'}`;
}
Enter fullscreen mode Exit fullscreen mode

Template literals are useful to create complicated and multi-line strings that can include single and double quotes, as well as variables and expressions (like the ternary operator above). It also eliminates the need to concatenate strings using the + operator.


Solution 2: Handling input that is not a string

The implementations from solution 1 pass the test cases provided by CodeWars, but they also pass when the arguments provided are numbers, null, undefined, objects and arrays for examples. This is likely not behavior we would want in a real life scenario, so let's add some code to handle cases where one or both of the arguments passed are not strings.

Examples
The following test cases have been added by me:

(2, 5), 'Invalid input'
({}, []), 'Invalid input'
(null, 'undefined'), 'Invalid input'
('null', 'undefined'), 'Hello guest'
Enter fullscreen mode Exit fullscreen mode

Variant 1: using if/else if statements
function greet (name, owner) {
  const isValidName = typeof name === "string";
  const isValidOwner = typeof owner === "string";
  const isValidArgs = isValidName && isValidOwner;

  if (!isValidArgs) return "Invalid input";
  else if (isValidArgs && name == owner) return "Hello boss";
  return "Hello guest";
}
Enter fullscreen mode Exit fullscreen mode

Notes:

1) The typeof operator is used to verify that the name and owner arguments are strings.

2) It is common practice to start variables storing a Boolean value with 'is'.

3) The 3 first lines can easily be reworked into 1 line. I just chose to split them up for this article to make it a little easier to follow along with what is going on.

4) The if statement uses the logical not (!) operator to return the string "Invalid input" if isValidArgs is false (meaning one or both of the arguments are not strings).


Variant 2: using a nested ternary operator
function greet (name, owner) {
  // variable declarations (deleted for legibility) 

  return !isValidParams 
    ? "Invalid input" 
    : isValidParams && name === owner 
      ? "Hello boss"
      : "Hello guest"
}
Enter fullscreen mode Exit fullscreen mode


Solution 3: Handling invalid input/strings

Besides arguments that aren't strings, there is another (edge) case that we want to consider: invalid strings such as empty strings and strings containing only white-space. Depending on the real-world context our code is implemented in, the rules on what is considered an invalid string might be less or more strict, but for the purposes of this exercise, we'll assume that both names (first, last and full) and usernames are allowed.

Test cases and solution
('nats_tech_notes', 'nats_tech_notes'), 'Hello boss'
('@Nat_A_Number', '12345'), 'Hello guest'
('', ''), 'Invalid input'
(' ', ' '), 'Invalid input'
(3, 5), 'Invalid input'
Enter fullscreen mode Exit fullscreen mode

The test cases above were added by me.

function greet (name, owner) {
  const regex = /^[\S]\w/;
  const isValidName = typeof name == "string" 
                      && regex.test(name);
  const isValidOwner = typeof owner == "string"
                       && regex.test(owner);
  const isValidArgs = isValidName && isValidOwner;

  // deleted code: same code from solution 2
}
Enter fullscreen mode Exit fullscreen mode

Notes:

1) To test the validity of the string, regular expressions are used.

2) The regular expression starts with the ^ assertion symbol, signifying the beginning of the string, followed by the \S character class to indicate no white-space. This ensures the string cannot start with or contain only white-space. It also contains the \w character class to verify the string contains at least 1 alphanumerical character. This combination allows for full/multiple names to be used which will contain white-space between each name.



Kata 2: Parse nice int from char problem

Instructions

You ask a small girl,"How old are you?" She always says, "x years old", where x is a random number between 0 and 9.

Write a program that returns the girl's age (0-9) as an integer.

Assume the test input string is always a valid string. For example, the test input may be "1 year old" or "5 years old". The first character in the string is always a number.


Breaking down the problem

Parameters
The function expects 1 parameter: a string.

The instructions specify the string will always be a valid string and the first character in the string will always be the age.

Returns

The function should return 1 value: a number, more specifically an integer.

Examples

The following test cases are provided by CodeWars.

("4 years old"), 4
Enter fullscreen mode Exit fullscreen mode

The first value is the string that will be passed to the function, followed by the integer that should be returned.


Solution 1: using parseInt()

function getAge(inputString){
  return parseInt(inputString);
}
Enter fullscreen mode Exit fullscreen mode

Notes:

The parseInt() function receives a string and returns an integer number if a number is present inside the string. If no number is present, it returns NaN.


Solution 2: using Number()

function getAge(inputString){
  return Number(inputString[0]);
}
Enter fullscreen mode Exit fullscreen mode

Notes:

1) The Number() function can convert values that are not numbers into numbers.

2) Just like arrays, the bracket notation can be used with strings to select a character at the specified index (string[index]).


Solution 3: using regex and the unary operator

function getAge(inputString){
  const numChar = inputString.match(/\d/);
  return +numChar;

  // alternatively:
  // return +inputString.match(/\d/);
}
Enter fullscreen mode Exit fullscreen mode

Notes:

1) Instead of the .test() function used in kata 1, solution 3, we use the .match() function to extract the part of the string that matches the regex pattern.

2) The \d character class is used to match digits.

3) The unary plus operator (+), much like the Number() function, converts values into numbers, if they aren't already numbers.


Kata 3: Correct the mistakes of the character recognition software

Instructions

When documents (especially pretty old ones written with a typewriter), are digitized character recognition software often make mistakes.

Your task is correct the errors in the digitized text. You only have to handle the following mistakes:

  • S is misinterpreted as 5
  • O is misinterpreted as 0
  • I is misinterpreted as 1

The test cases contain numbers only by mistake.


Breaking down the problem

Parameters
The function receives one parameter: a string.

The string contains both letters and numbers.

Returns
The function should return one value: a string containing only letters.

Examples
The following test cases are provided by CodeWars.

"L0ND0N" => "LONDON"
"DUBL1N" => "DUBLIN"
"51NGAP0RE" => "SINGAPORE"
"BUDAPE5T" => "BUDAPEST"
PAR15" => "PARIS"
Enter fullscreen mode Exit fullscreen mode

Solution 1: Creating a new string from an array

function correct(string) {
  const arr = [];
  string.split('').forEach(char => {
    if (char === '5') { arr.push('S'); } 
    else if (char === '0') { arr.push('O'); } 
    else if (char === '1') { arr.push('I'); } 
    else { arr.push(char); }
  });
  return arr.join('');
}
Enter fullscreen mode Exit fullscreen mode

Notes:

1) A new empty array is initiated so letters can be pushed into it.

2) The original string is then turned into an array using the .split() method so we can loop over the array using the .forEach array method.

3) Inside the loop, we verify if the current character is one of the specified numbers and if it is, the correct letter is pushed into the array. If the character is already a letter, the letter is pushed into the array.

4) We then turn the array back into a string using the .join() method.


Solution 2: Replacing each character separately

function correct(string) {
  return string.replace(/5/g, 'S')
               .replace(/0/g, 'O')
               .replace(/1/g, 'I');
}
Enter fullscreen mode Exit fullscreen mode

Notes:

1) The .replace() method, as the name suggests, replaces the text/pattern specified in its first parameter with the text/pattern specified in the second parameter and returns a new string.

2) The method is called 3 times on the same object (string) with the help of method chaining.

3) The /g flag at the end of the regular expression stands for global and is used to match not just the first occurrence, but all occurrences in the string.


Solution 3: Replacing all characters at once

function correct(string) {
  const chars = {
    '5': 'S', 
    '0': 'O', 
    '1': 'I'
  };
  return string.replace(/[501]/g, key => chars[key]);
}
Enter fullscreen mode Exit fullscreen mode

Notes:

1) First an object is created to hold the incorrect number characters and their replacements.

2) The .replace() method is then used to replace all incorrect characters at once instead of one by one. To achieve this, a function is provided as the second parameter that grabs the correct replacement from the object.


What solution did you implement?
What solution did you find the most elegant?
What solution did you have trouble understanding?

Let me know, I'd love to hear from you!

I hope you found this article helpful.


Connect with me on ...
Twitter
GitHub

💖 💪 🙅 🚩
nats_tech_notes
Nat's Tech Notes

Posted on June 1, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related