Graystripe Graystripe - 1 year ago 226
Swift Question

Dictionary with Multiple Keys for a Single Value - Swift

I'm attempting to create an application that maps English letters to various RNA codons (sequences of A, C, U, or G in groups of 3), according to a predetermined mapping.

To convert from English to RNA, the dictionary looks like this:

var englishTomRNA: [Character: [String]] = ["A": ["UUU", "UAC"],
"Q": ["UUA"],
"S": ["UCU", "UCC", "UCA"]]


As you can see, there are multiple values for any given English character (representing the biological characteristic of a polygenetic genotype), of which I have no issue converting to.

What I do have an issue implementing, however, is the dictionary to covert the other way. I originally used
switch: case
statements like so:

for codon in preEnglishInputString {
switch codon {
case "UUU", "UAC": inEnglish.append("A")
case "UUA": inEnglish.append("Q")
case "UCU", "UCC", "UCA", "UCG": inEnglish.append("S")

However, now I wish to convert the RNA -> English conversion function to use dictionaries to be more inline with this implementation.

I cannot figure out how to have multiple keys for a single value (the reverse of having multiple values for a single key, as used above.)

The following, when attempted, doesn't work:

let mRNAtoEnglish: [[String] : Character] =
[["UUU", "UAC"] : "A",
["UUA", "UUG"] : "Q",
["UCU", "UCC", "UCA"] : "S"]

with the compiler yelling
Type [String] does not conform to protocol 'Hashable'

Am I simply using the wrong data structure, or is there some syntax that i'm not aware of? (I'm fairly new to Swift)


Answer Source

Use Swift's higher order functions:

var englishTomRNA: [Character: [String]] = ["A": ["UUU", "UAC"],
                                            "Q": ["UUA"],
                                            "S": ["UCU", "UCC", "UCA"]]
var inEnglish = ""

// Sample Input
let preEnglishInputString = ["UUA", "UCC", "UUU"]

for codon in preEnglishInputString {
    let english = englishTomRNA
                        .filter { $0.1.contains(codon) }

print(inEnglish) // QSA

This works with 2 assumptions:

  1. Each codon code is mapped to exactly 1 English character
  2. Every codon code in preEnglishInputString is valid and can be found in your englishTomRNA dictionary.

What it does:

.filter { ... } finds any key-value pair that contains the codon. When you call filter on a Dictionary, it degenereates into a tuple of (key, value) so $0.1 refers to the value. filter always return an array of matches, even if it finds only one match.

first! gets the first match from the list (assumption 1). The exclamation mark assumes that a match is always found (assumption 2).

.0 gets the key of the key-value pair, which the English character.