Day 2/30 Days of CodeWars: JavaScript Edition
Nat's Tech Notes
Posted on June 1, 2022
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'
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).
This is a similar approach to variant 1, with 2 main differences: 1) The 2) There is no Variant 2: using an if statement only
function greet (name, owner) {
if (name === owner) return "Hello boss";
return "Hello guest";
}
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.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";
}
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 Variant 4: using template literal syntax
function greet (name, owner) {
return `Hello ${name === owner ? 'boss' : 'guest'}`;
}
+
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'
Notes: 1) The typeof operator is used to verify that the 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 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";
}
name
and owner
arguments are strings
. 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"
}
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.
The test cases above were added by me. Notes: 1) To test the validity of the string, regular expressions are used. 2) The regular expression starts with the 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'
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
}
^
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
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);
}
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]);
}
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/);
}
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 as5
-
O
is misinterpreted as0
-
I
is misinterpreted as1
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"
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('');
}
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');
}
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]);
}
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.
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
November 29, 2024