Hamming (coding challenge) Part I
Published: 2022-11-29 12:54:46

---
title: Hamming (coding challenge) Part I
published: true
description: Coding challenges solved on Exercism platform
tags: 
# Use a ratio of 100:42 for best results.
# published_at: 2022-11-28 08:46 +0000
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7stlbcnpswak4ynjifgw.jpg
---

Hi Folks!! it's been a while since I wrote an article on what I have been doing lately. Currently I'm job searching, and in a bid to keep myself up to date, I decided that I will be solving coding challenges on the [Exercism](https://exercism.org/tracks/ruby/exercises/raindrops) website daily using `Ruby` and also refactoring the solved code for readability.

For every challenge solved, I will be publishing my initial code iteration and the fully optimised version. I will also be describing lessons learnt while solving the coding challenge.
So hold on to your seats and enjoy the ride!

For starters, this article's focus will be on the [Hamming] 
 (https://exercism.org/tracks/ruby/exercises/hamming) coding challenge. According to [Exercism](https://exercism.org/tracks/ruby/exercises/raindrops), the anticipated lessons to be learnt are `Advanced Enumeration`, `Loops`,`Conditionals`, and `Raising Exceptions`. However I'm sure I learned more after I was done refactoring the initial solution.

For the Hamming challenge here is an abridged version of the instruction introducing the challenge:

> Calculate the Hamming Distance between two DNA strands.
  DNA strands are read using the letters C,A,G and T and Two 
  strands might look like this:
  ```
   GAGCCTACTAACGGGAT
   CATCGTAATGACGGCCT
   ^ ^ ^  ^ ^    ^^
  ```
> They have 7 differences, and therefore the Hamming Distance is 7.
  The Hamming distance is only defined for sequences of equal 
  length, so an attempt to calculate it between sequences of 
  different lengths should not work.

With the above instructions I set out to solve the challenge.
My initial solution  produced the below code block:


```rb
class UnequalStrandError < ArgumentError
  def initialize(error_message = 'Strands must be of equal length')
    super
  end
end

class Hamming
  def self.compare_strands(first_strand, second_strand)
    first_array = first_strand.split('')
    second_array = second_strand.split('')
    first_array.each_with_object(container = []).with_index do |(item), index|
      second_array.at(index) != item and container << item or nil
    end
  end
  def self.check_strand_size(first_strand, second_strand)
    raise UnequalStrandError if first_strand.empty? && second_strand.size.positive?
    raise UnequalStrandError if first_strand.size.positive? && second_strand.empty?
    raise UnequalStrandError if first_strand.size > second_strand.size
    raise UnequalStrandError if first_strand.size < second_strand.size
  end
  def self.empty_strands(first_strand, second_strand)
    check_strand_size(first_strand, second_strand)
    return 0 if first_strand.empty? && second_strand.empty?
  end
  def self.compute(first_strand, second_strand)
    empty_strands(first_strand, second_strand)
    if first_strand.length == 1 && second_strand.length == 1
      first_strand != second_strand and 1 or 0
    else
      compare_strands(first_strand, second_strand).length
    end
  end
end

```
Well, the above code block got everything working, however we all know there are ways we can optimise this code block. Before I go into refactoring the code, would it not be okay if I explain what I did?

let's start with `class UnequalStrandError`:

```rb
class UnequalStrandError < ArgumentError
  def initialize(error_message = 'Strands must be of equal length')
    super
  end
end
```

The `UnequalStrandError` class is inheriting from the `ArgumentError` class, and I am initialising the `error_message` as 'Strands must be of equal length'. This is the message that will be displayed anytime the strands to be compared are of unequal size.

The `UnequalStrandError` class is also being raised in class `Hamming` see `self.check_strand_size`:

```rb
class Hamming

  def self.compare_strands(first_strand, second_strand)
    first_array = first_strand.split('')
    second_array = second_strand.split('')
    first_array.each_with_object(container = []).with_index do |(item), index|
      second_array.at(index) != item and container << item or nil
    end
  end

  def self.check_strand_size(first_strand, second_strand)
    raise UnequalStrandError if first_strand.empty? && second_strand.size.positive?
    raise UnequalStrandError if first_strand.size.positive? && second_strand.empty?
    raise UnequalStrandError if first_strand.size > second_strand.size
    raise UnequalStrandError if first_strand.size < second_strand.size
  end

  def self.empty_strands(first_strand, second_strand)
    check_strand_size(first_strand, second_strand)
    return 0 if first_strand.empty? && second_strand.empty?
  end

  def self.compute(first_strand, second_strand)
    empty_strands(first_strand, second_strand)
    if first_strand.length == 1 && second_strand.length == 1
      first_strand != second_strand and 1 or 0
    else
      compare_strands(first_strand, second_strand).length
    end
  end
end
```

Now to explain what is happening inside class `Hamming`. This class was setup to have class methods. Class methods (in the context of the Hamming class) do not require that a `Hamming` object is created before using any method in class `Hamming`. That is why you can see the `self` prefix before any method. 

The `def self.check_strand_size(first_strand, second_strand)` raises the `UnequalStrandError` if one of arguments are `empty?` and if the other is not empty. it also raises error if the argument sizes are not the same.

The `def self.empty_strands(first_strand, second_strand)` returns 0 if first_strand.empty? && second_strand.empty? and also calls `def self.check_strand_size`

`def self.compare_strands(first_strand, second_strand)` is spliting the strings into an array. Using the first array produced, I am comparing if each item iterated on is not the same as each item in the second array produced:

```rb
first_array.each_with_object(container = []).with_index do |(item), index|
      second_array.at(index) != item and container << item
    end
```
if it is not the same, then the item is pushed and saved into another array called `container`

The last method is `def self.compute(first_strand, second_strand)`. This method calls other methods I have defined earlier on like `empty_strands(first_strand, second_strand)` and also `compare_strands(first_strand, second_strand)`. Since the answer required for the task is a number/integer, and I know that `compare_strands` returns an array if non-matching items, all I did  was to get the size of the array and that is what produces the answer for the task. 

So for example if I supply the following arguments to Hamming's compute method like so:

```rb
Hamming.compute('GAGCCTACTAACGGGAT', 'CATCGTAATGACGGCCT')
```
The expected answer is:
```rb
7
```

This code works like it is suppose to but there is still need to refactor the code for readability, and I will be doing this in Part II of this article. From the looks of things it seems I was able to match Exercism's expectations on what I will learn by solving this challenge. I used `Advanced Enumeration`, `Loops`,`Conditionals`, and also raised `Exceptions`. However more lessons will be learned in [Part II](https://dev.to/tundeiness/hamming-coding-challenge-part-ii-452i) of this article.