Learning programming so sorry for a beginner question! Here I have a code that works in my sublime text editor but raises an exception on Coderbyte, which from what I know uses Ruby 1.8.7. I suspect it might have to do with the different versions of Ruby. Would be helpful to understand what is going wrong. Thanks for replying!
Exception raised is:
(eval):9: undefined method `keys' for []:Array (NoMethodError) from (eval):4:in `each' from (eval):4:in `LetterCountI' from (eval):23
def LetterCountI(str)
str = str.split
repeating_letters = []
str.each do |word|
word = word.split("")
letters = Hash.new(0)
word.each { |letter| letters[letter] += 1 }
selected_letters = letters.select { |key, value| value > 1 }
repeating_letters << selected_letters.keys.length
end
if (repeating_letters.select {|l| l >= 1}).empty?
return -1
else
max = repeating_letters.max
p repeating_letters
return str[repeating_letters.index(max)]
end
end
Yes, it's the versions. In Ruby 1.8.7 hash.select returns an Array, which has no keys method. In later versions select returns a Hash.
Related
Been trying to solve this Codewars problem with Ruby (https://www.codewars.com/kata/57061b6fcb7293901a000ac7/train/ruby) but it keeps giving me this error and I don't know the reason.
main.rb:10:in `head_smash': undefined method `each' for "":String (NoMethodError)
from main.rb:80:in `block in <main>'
from /runner/frameworks/ruby/cw-2.rb:55:in `block in describe'
from /runner/frameworks/ruby/cw-2.rb:46:in `measure'
from /runner/frameworks/ruby/cw-2.rb:51:in `describe'
from main.rb:17:in `<main>'
My code to solve this problem is this one:
def head_smash(arr)
arr1 = []
if arr == []
return 'Gym is empty'
elsif arr.is_a?(Integer) == true
return 'This isn\'t the gym!!'
else
arr.each do |i|
arr1.append(i.gsub(/O/, ' '))
end
end
return arr1
end
Thanks!
It is probably this test (see line #64 in the test for that exercise) that makes your code fail:
Test.assert_equals(head_smash(''),'Gym is empty')
In that test, the input is an empty String but your code doesn't check for string types and at least handles empty Strings. Instead, your code calls arr.each when arr is a string.
A very simple approach to fix this specific issue would be to change the first condition
if arr == []
to any of the following
if arr == [] || arr == ''
if [[], ''].include?(arr)
if arr.respond_to?(:empty?) && arr.empty?
I am trying to iterate through a folder, select all the files that end in .bowtie.txt, count the number of lines, and then write the number of lines with the filename it came from, to a hash (or even better an external csv but a hash will do for now). I was provided with this solution
Dir['/Volumes/.../*.bowtie.txt'].inject({}) do |memo, file|
memo[file] = File.readlines(file).select do |line|
line =~ /^[0-9]+\s*(\+|\-)/ # only those, matching
end.count
puts memo
end
however this has to odd behaviour of selecting a file, then giving me one hash before it fails as follows:
Me:~ me$ ruby /Users/me/Desktop/SequencingScripts/Plots/GeneralScripts/DistinctPositions.rb
{"/Volumes/SeagateBackupPlusDriv/SequencingRawFiles/TumourOesophagealOCCAMS/SequencingScripts/3finalcounts/SLX-9279.GoodDysplasia.FSeqA_BEST2_NEW_0204_LessThan10PCent_HGD_.fq.gz.bowtie.txt"=>31312}
/Users/me/Desktop/SequencingScripts/Plots/GeneralScripts/DistinctPositions.rb:5:in `block in <main>': undefined method `[]=' for nil:NilClass (NoMethodError)
from /Users/me/Desktop/SequencingScripts/Plots/GeneralScripts/DistinctPositions.rb:4:in `each'
from /Users/me/Desktop/SequencingScripts/Plots/GeneralScripts/DistinctPositions.rb:4:in `inject'
from /Users/me/Desktop/SequencingScripts/Plots/GeneralScripts/DistinctPositions.rb:4:in `<main>'
when I don't use puts memo I don't get an error but I also don't get any output to the terminal. How do I get what I want?
In order to use inject in this context, you need to return your memo object at the end. So in your example, that would look like:
Dir['/Volumes/.../*.bowtie.txt'].inject({}) do |memo, file|
memo[file] = File.readlines(file).select do |line|
line =~ /^[0-9]+\s*(\+|\-)/ # only those, matching
end.count
puts memo
memo
end
Here's a contrived example to illustrate the same error & resolution:
[1] pry(main)> [1, 2, 3].inject({}) { |hash, num| hash[num] = 1 }
NoMethodError: undefined method []= for 1:Fixnum
[2] pry(main)> [1, 2, 3].inject({}) { |hash, num| hash[num] = 1; hash }
=> {1=>1, 2=>1, 3=>1}
if you use inject your block should ALWAYS return updated memo in the last line. In your case during the second iteration memo is equal to last line so to the result of puts memo
Dir['/Volumes/.../*.bowtie.txt'].inject({}) do |memo, file|
memo[file] = File.readlines(file).select do |line|
line =~ /^[0-9]+\s*(\+|\-)/ # only those, matching
end.count
puts memo
memo
end
I've been reading the Ruby docs, and looking at some other posts on the issue, but I am still wondering about this:
#counts each number in an array once
array = [1,1,2,5,3,2,5,3,3,3]
numbers = {}
array.each { |num| numbers[num] += 1 }
=> in `block in mode': undefined method `+' for nil:NilClass (NoMethodError)
In the Hash documentation the default value for a Hash is nil, which is why I am getting this error I assume. Is there a better way to insert each key/(value += 1) into the numbers array?
Try passing a default value to your new hash as such
numbers = Hash.new(0)
You can explicitly do it this way as well:
array.each { |num| numbers[num] = (numbers[num] || 0) + 1 }
Variant with inject and Hash.new(0)
numbers = [1,1,2,5,3,2,5,3,3,3].inject(Hash.new(0)){|numbers, number| numbers[number] +=1; numbers}
Aside from using the Hash default, you could also try something with group_by:
array = [1,1,2,5,3,2,5,3,3,3]
numbers = Hash[*array.group_by { |i| i }.flat_map { |k, v| [k , v.size] }]
There's probably a better way if you play around with it some.
I am really new at ruby. I have created a function to count occurrences of words in a string. However, I am getting NoMethodError for + all the time. I searched, tried different variations, but couldn't solve the problem. Here is the code:
def count_words(str)
str_down = str.downcase
arr = str_down.scan(/([\w]+)/).flatten
hash = Hash[]
arr.each {|x| hash[x] += 1 }
(hash.sort_by {|key, value| value}.reverse)
end
Here is the error:
NoMethodError: undefined method `+' for nil:NilClass
from ./***.rb:14:in `count_words'
from ./***.rb:14:in `each'
from ./***.rb:14:in `count_words'
from (irb):137
Change
hash = Hash[]
arr.each {|x| hash[x] += 1 }
To
hash = {}
arr.each {|x| hash[x] =0 unless hash[x]; hash[x] += 1 }
OR
hash = Hash.new(0)
arr.each {|x| hash[x] += 1 }
EXPLAINATION
hash = {}
hash[1] = "example1" #ASSIGNMENT gives hash = {1: "example1"}
p hash[2] #This gives `nil` by default, as key is not present in hash
To give default value to the key which is not present in hash we have to do the following:
hash = Hash.new("new value")
p hash #Gives {}
p hash[4] #gives "new value"
In the first iteration, h[x] is nil. Trying to add 1 to nil throws error. Setting the initial value of h[x] to 0 will solve the issue.
arr.each {|x| hash[x]||=0; hash[x] += 1 }
instead of
arr.each {|x| hash[x] += 1 }
I have a method that returns a hash, or nil:
def person_of_age(age)
some_hash = #array_of_hashes.select { |h| h.age == age }.last
return some_hash
end
I want to use this hash like so:
my_height = 170
my_age = 30
if my_height < self.person_of_age(my_age)['height']
puts "You are shorter than another person I know of the same age!"
end
Now, if the hash returns nil, ruby doesn't like me using ['height']:
undefined method `[]' for nil:NilClass (NoMethodError)
Fair enough, but how can I use ||= to avoid this problem?
If the method does return nil, let's just say I want the 'height' to be 0.
I have tried things along the lines of, to no avail:
if my_height < self.person_of_age(age)||={ 'height' => 0 }['height']
#...
if my_height < self.person_of_age(age)['height'] ||= 0
Clearly my example is running a little thin, and there are other ways around this problem, but if ||= can be used I'd love to know how.
Thank you!
obvious:
(self.person_of_my_age(age) || {'height' => 0})['height']
but somehow this does not feel so right
You can do it like this:
if my_height < self.person_of_age(age).nil? ? 0 : self.person_of_age(age)['height']
Or you can rewrite person_of_age method:
def person_of_age(age)
some_hash = #array_of_hashes.select { |h| h.age == age }.last || {:height => 0}
return some_hash
end
or simpler
def person_of_age(age)
#array_of_hashes.select { |h| h.age == age }.last || {:height => 0}
end
I'm not sure if this helps in your circumstance, but one capability Hash has is
hash.fetch(key) { block_for_code_evaluated_if_key_absent }