Swift Morse Decoder

Viewed 239
var morseText = ".... . -.--   - .... . .-. ."

In English, it is "hey there"

var morseCodeArray: [String] = []

This is the array where individual morse code letters will be stored.

var currMorse: String = ""

currMorse is used to keep track of the current Morse letter.

for char in morseText {
  if char != " " {
    currMorse.append(char)
  } else {
    switch currMorse {
      case "": 
        currMorse += " "
      case " ":
        morseCodeArray.append(" ")
        currMorse = ""
      default: 
        morseCodeArray.append(currMorse)
        currMorse = ""
    }
  }
}
morseCodeArray.append(currMorse)

Could someone please explain what exactly is happening in the switch statement?

I know Apple wants switch statements to be exhaustive, and hence we have three cases here.

What I do not understand is the purpose of case #1 i.e. case "":

Why do we need this null/nil option? And even if we do, why do we then need to do this -> currMorse += " "

My programming-novice brain thinks we only need case #2 i.e. case " ":

Because all the other char options (like dots, dashes in morseText) will be covered by the if statement

4 Answers

In the morse code a single space is a letter separator.

The empty string case is to detect a word separator, technically "does the word start with a space character" .

In the example string it considers the extra space between y and t


You can accomplish the same with components(separatedBy and reduce

let morseCodeArray = morseText.components(separatedBy: " ").reduce([String]()) { (result, string) -> [String] in
    if result.last?.isEmpty == true && string.isEmpty { // replace two consecutive empty items with one space
        return result.dropLast() + [" "]
    } else {
        return result + [string]
    }
}

Generally when you don't unerstand why a certain piece of code is required or why it is acting the way it is, I suggest running your code in a debugger to understand what exactly is going on.

Add breakpoints at all branch statements and go through each line of code and each iteration to understand the effect of each line.

I just ran your code in a debugger. Wrote it in JS but it shouldn't matter. Here's what is happening:

  1. case "" is required because there's a point in your morseText right after -.-- whereby a null string is encountered. The case takes this as null and effectively converts it to a string with a whitespace with this statement: currMorse += " "
  2. case: " " is required to deal with the newly created whitespace in the previous iteration. So it adds the " " as an element to your morseCodeArray array.
for char in morseText {                  // iterates thru all characters in morseText
  if char != " " {                       // .. in case char is not one space character 
    currMorse.append(char)               // .. .. append character to currMorse
  } else {                               // .. otherwise case char must be space
    switch currMorse {                   // .. .. but what if currMorse {
      case "":                           // .. .. .. contains nothing yet?
        currMorse += " "                 // .. .. .. .. add space to currMorse 
                                         // .. .. .. .. no need to empty currMorse here
      case " ":                          // .. .. .. contains space allready?
        morseCodeArray.append(" ")       // .. .. .. .. append space to morseCodeArray
        currMorse = ""                   // .. .. .. .. and empty currMorse
      default:                           // .. .. .. if all above fail
        morseCodeArray.append(currMorse) // .. .. .. .. just append to morseCodeArray 
        currMorse = ""                   // .. .. .. .. and empty currMorse
    }                                    // .. .. } end of switch
  }                                      // .. } end of else
}                                        // } end of for loop
morseCodeArray.append(currMorse)         // add all valid currMorse to morseCodeArray

so it parses morseText and checks each character for being not spaces, so it must be a letter, but if it contains a space then check if currMorse is empty and fill one space in currMorse to be added in the next iteration or at the end. Or currMorse is already one space? thats fine, so add one space to the result and empty currMorse (again), otherwise just append what currMorse contains yet and empty currMorse so it can't be added later, so only letters and spaces are added at the end.

This has a side effect, very obviously spaces from morseText are not passed to morseCodeArray directly and exchanged with spaces from currMorse.

Switch statement examines the value of the currMorse variable. If you take a look at the default statement it does two things:

  1. appends the current morse to the array
  2. resets the value of currMorse

If there would be no reset, the currMorse would grow in every further step and you would end up with something like this (just comment out the line):

....
.....
.....-.--
.....-.--
.....-.--
.....-.---
.....-.---....
.....-.---.....
.....-.---......-.

The switch case case "": is pretty much checking for the following case:

char == " " && currMorse == ""

If you put a breakpoint, you will see this happens exactly once.

Related