Reputation: 702
I've been learning Swift 3 syntax lately and I thought that a good first project would be a Vigenère Cipher. So I've begun to create a script for it in Playground.
The issue is that I keep getting an error when I call the method and I know what the mistake is, it has to do with how I am calling my Dictionary value and the fact that I am unwrapping it, But I don't know what else to do about it. Any ideas?
import Foundation
let alphabet: [String: Int] = ["a": 0, "b": 1, "c": 2, "d": 3, "e": 4, "f": 5, "g": 6, "h": 7, "i": 8,
"j": 9, "k": 10, "l": 11, "m": 12, "n": 13, "o": 14, "p": 15, "q": 16,
"r": 17, "s": 18,"t": 19, "u": 20, "v": 21, "w": 22, "x": 23, "y": 24, "z": 25]
let alphabet2: [Int: String] = [0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", 7: "h", 8: "i",
9: "j", 10: "k", 11: "l", 12: "m", 13: "n", 14: "o", 15: "p", 16: "q",
17: "r", 18: "s", 19: "t", 20: "u", 21: "v", 22: "w", 23: "x", 24: "y", 25: "z"]
var mess = "I once saw a big mosquito"
var key = "pass"
func cipher(message: String, key: String) -> String{
var code: [Int] = [] // will hold the encripted code
// removes whietspace from message and key
let trimmedMessage = message.trimmingCharacters(in: NSCharacterSet.whitespaces)
let trimmedKey = key.trimmingCharacters(in: NSCharacterSet.whitespaces)
// Sets the key the same size as the message
let paddedTrimmedKey = trimmedKey.padding(toLength: message.characters.count, withPad: trimmedKey, startingAt: 0)
// separates the message and key into individual characters
let charTrimmedMessage = Array(trimmedMessage.characters)
let charPaddedTrimmedKey = Array(paddedTrimmedKey.characters)
// Compare the values in the key to the message and scrambles the message.
var i = 0
for chr in charTrimmedMessage{
code.append((alphabet[String(chr)]! + alphabet[String(charPaddedTrimmedKey[i])]!) % 26) // <- I think the error comes from this line. Maybe the exclamation marks?
i += 1
}
var cyphr: String = "" // this will hold the return String
// I think I could have used some sort of "join" function here.
for number in code{
cyphr = cyphr + alphabet2[number + 1]!
}
return cyphr
}
cipher(message: mess, key: key) // <--- this returns an error, no clue why. The code works and compiles great.
I get this error:
If you could let me know any pointer on how to improve my code to avoid things like this even better.
Upvotes: 0
Views: 262
Reputation: 2459
Your alphabet
dictionary does not have lookup values for the letters "I", " " which are contained in your plain text. The cipher function fails because of this (since it is force unwrapping an optional which is nil)
If you initialize the mess
variable, you will get the cipher to work
var mess = "ioncesawabigmosquito"
cipher(...) -> "ypgvuttpqcbzcpljkjmh"
Either
alphabet
lookup dictionary. E.g. add ' ' as a valid input, update alphabet2
to provide reverse lookup, and change the mod factor from 26 to 27 (to account for new ' ' character)OR
To trim your input to only include valid letters in the alphabet, you could try:
let validSet = CharacterSet.init(charactersIn: alphabet.keys.joined())
var messTrimmed = mess.trimmingCharacters(in: validSet.inverted)
Note that doing this mean losing information from the original message
Another bug:
The line cyphr = cyphr + alphabet2[number+1]!
should be cyphr = cyphr + alphabet2[number]!
.
It's not correct to add 1 to number since the values in the code array are computed mod 26, and the max key value in alphabet is 25. It will cause an exception when force unwrapping to a non-existent key.
E.g. try cipher(message: "ypgvuttpqcbzcpljkjmh", key: "pass")
to make it fail.
Footnote
As a complete aside, here's a variant of the cipher function (I have not sanitized the inputs; just showing how the code could be written in a functional way)
func cipher2(message: String, key: String) -> String {
return
message
.characters
.enumerated()
.map { ($0, String($1)) }
.map {
(
alphabet[$1]!
+ alphabet[String(key[key.index(key.startIndex, offsetBy: ($0 % key.characters.count))])]!
) % 26
}
.map { alphabet2[$0]! }
.joined()
}
Upvotes: 2