Reputation: 115
So I am working on a small assignment to transcribe DNA strands to RNA strands. My current code looks like this:
class Complement
def self.of_dna(str)
dna_rna = { 'G' => 'C', 'C' => 'G', 'T' => 'A', 'A' => 'U' }
rna = []
str.scan(/[GCTA]/).each do |x|
rna << dna_rna[x]
end
rna.join('')
end
end
It works perfectly, except for in one situation. If a DNA strand is passed that is partially correct, for example ACGTXXXCTTAA
, my method will translate the DNA to RNA and just leave out the X's, giving me a result of UGCAGAAUU
rather than just ""
. How can I make it so the loop will fail and exit when it receives a letter that isn't DNA related?
EDIT: The test I am trying to get to pass looks like this:
def test_dna_correctly_handles_partially_invalid_input
# skip
assert_equal '', Complement.of_dna('ACGTXXXCTTAA')
end
I attempted @Holger Just's idea from below, and received this error:
1) Error:
ComplementTest#test_dna_correctly_handles_completely_invalid_input:
ArgumentError: ArgumentError
/Users/LukasBarry/exercism/ruby/rna-transcription/rna_transcription.rb:6:in `block in of_dna'
/Users/LukasBarry/exercism/ruby/rna-transcription/rna_transcription.rb:5:in `each'
/Users/LukasBarry/exercism/ruby/rna-transcription/rna_transcription.rb:5:in `of_dna'
rna_transcription_test.rb:43:in `test_dna_correctly_handles_completely_invalid_input'
The usual failure I've been getting from the above method is this:
1) Failure:
ComplementTest#test_dna_correctly_handles_partially_invalid_input [rna_transcription_test.rb:48]:
Expected: ""
Actual: "UGCAGAAUU"
Any help would be greatly appreciated.
Upvotes: 1
Views: 94
Reputation: 27793
Try this
class Complement
def self.of_dna(str)
return "" if str =~ /[^GCTA]/
...
end
end
Fun fact, you don't even need a loop to replace characters
str = 'GATTACA'
str.tr('ATCG', 'UAGC')
# => 'CUAAUGU'
Is all you need.
Upvotes: 4
Reputation: 80051
I prefer using Hash#fetch
for this because it'll raise a KeyError
for you on mismatch, allowing you to write less code that validates inputs (i.e., less defensive programming), which I think is more valuable than cleverness (in which case I would recommend String#tr).
class DNA
TranslationMap = { 'G' => 'C', 'C' => 'G', 'T' => 'A', 'A' => 'U' }
attr_reader :dna
def initialize(dna)
@dna = dna
end
def to_rna
dna.each_char.map do |nucleotide|
TranslationMap.fetch(nucleotide)
end.join('')
rescue KeyError
return false
end
end
Feel free to adapt what happens when the error is rescued to fit your needs. I recommend raising a more specific exception (e.g. DNA::InvalidNucleotide
) for the caller to handle.
In use:
dna = DNA.new 'GCTA'
# => #<DNA:0x007fc49e903818 @dna="GCTA">
dna.to_rna
# => "CGAU"
dna = DNA.new 'ACGTXXXCTTAA'
# => #<DNA:0x007fc49f064800 @dna="ACGTXXXCTTAA">
dna.to_rna
# => false
Upvotes: 1
Reputation: 55803
You can also match X
in your regex and perform some erorr handling if it is found in the string. This could look something like this:
class Complement
def self.of_dna(str)
dna_rna = { 'G' => 'C', 'C' => 'G', 'T' => 'A', 'A' => 'U' }
rna = []
str.scan(/[GCTAX]/).each do |x|
return '' if x == 'X'
rna << dna_rna[x]
end
rna.join('')
end
end
Upvotes: 1