Day 05 - Code Challenges on Christmas🎅

🌟Merry Christmas🎄, everyone! Hope you all have a wonderful Christmas with friends and family!

As a learner, there is no excuse for me to skip practice even on this special day of the year. I decided to finish some katas before my Christmas feast. Here are what I learnt today:

Highest Scoring Word

kata link: https://www.codewars.com/kata/57eb8fcdf670e99d9b000272/ruby

This kata asked me to calculate and compare the score of each word in an array based on a = 1, b = 2, ... z = 26 , then return the word with the highest marks.

My way to solve this kata

Step 1 - Create a hash with marking scheme

The first thought in my mind was to create a hash which includes the scores from a to z . And I can add up the scores by searching in the hash.

And this is how I created the hash:

alphabet_hash = {}
("a".."z").each_with_index do |alphabet, i| 
alphabet_hash[alphabet] = i + 1 
end
# { "a" => 1, "b" => 2, ... "z" => 26 }

I have to keep the score and also the word so that I could return the word with the highest score, so I created another hash to set key as the word and value as the score.

Step 2 - Create a hash with words & scores

Split and loop through the array which is the input of the function, then add up the scores.

char_table = {}
Array.split.each do |word|
    char_table[word] = 0
    word.split("").each do |char|
        char_table[word] += alphabet_table[char]
    end
end
# { "sample" => 48, "is" => 24 ... }

Step 3 - Return the word with the highest score

The last step is to look for the word (key)with the highest scores (values) in the char_table .

return char_table.key(char_table.values.max)

What did I learn from others?

I found something new to me in the solutions from others. Here are some methods I think it's quite useful and worth learning.

Array.max_by()

There is a method which can return the object in enum that gives the maximum value from the given block.

If the n argument is given, maximum n elements are returned as an array. These n elements are sorted by the value from the given block, in descending order.

a = "albatross dog horse"
a.max_by { |x| x.length }   #=> "albatross"
a.max_by(2) {|x| x.length } #=> ["albatross", "horse"]

String.ord()

ord() returns the codepoint of the first character of the string. For example, the codepoint of the alphabet a is 97. We can deduct the codepoint by 96 in order to get a => 1.

Other than ord() , String.sum() can produce the same result.

"a".ord #=>    97
"a".sum #=>    97

"abc".ord #=>    294

Another way of solving this kata

Instead of creating a hash to store the entire marking scheme, we can directly calculate the score by using sum() to get the codepoint of a string and then minusing 96 from it.

At the end, use max_by to pick the highest one.

def high(x)
  x.split.max_by{ |word| word.sum - 96 * word.length }
end

Count the smiley faces!

kata link: https://www.codewars.com/kata/583203e6eb35d7980400002a/ruby

This kata asked me to check how many correct smiley faces in the input array and then return the number.

My way to solve this kata

I used a simple but not very clean way to solve this kata, with several include? methods to check the input array.

Here are the codes I submitted:

def count_smileys(arr)
  # Variable
  num_of_valid = 0
  # Loop through each smiley face
  arr.each do |face|
    # Check if eyes are valid
    next if !face.include?(":") and !face.include?(";") 
    # If the face includes a nose
    if face.length == 3
      next if !face[1].include?("-") and !face[1].include?("~")
    end
    # Check if mouth is valid
    next if !face.include?(")") and !face.include?("D")
    # Add 1 when the face passes all tests above
    num_of_valid += 1
  end
  # return the number of valid faces
  return num_of_valid
end

What did I learn?

We can control the flow by using next or break when each() method is looping through an array.

Next

By combining next and if, you can immediately skip the current element and go to the next element in the array.

It is super handy in this kata, as I don't want to end the looping just because one face doesn't match with the requirements

Break

If you want to stop the looping, you can use break instead of next .

Operator =~

When I check the solutions from others, I found many of them used =~ operator in their codes.

Here is one of those solutions:

def count_smileys(arr)
  arr.count { |e| e =~ /(:|;){1}(-|~)?(\)|D)/ }
end

I found the explanation on Stackoverflow:

\=~ is an operator for matching regular expressions, that will return the index of the start of the match (or nil if there is no match).

By specifying strings and grouping with RegEx, you can use =~ to filter out all strings which don't match with what you want.

Did you find this article valuable?

Support Terry Cheng by becoming a sponsor. Any amount is appreciated!