Home > Software engineering >  Ruby - Trying to compare 2 strings in ruby but getting an error. Unable to figure out what to do
Ruby - Trying to compare 2 strings in ruby but getting an error. Unable to figure out what to do

Time:10-05

puts "Enter the string"

str = gets.chomp.split("")

i =0  
j = i 1  
l = str.length-1  
for i in str[i..l]  
  k = 0  
  for j in str[j..l-1]  
    if i.bytes==j.bytes  
      k =1  
    end   
    # p i  
    # p j  
  end  
end  
Error   
Traceback (most recent call last):   
    2: from task4.rb:155:in `<main>'   
    1: from task4.rb:155:in `each'   
task4.rb:157:in `block in <main>': bad value for range (ArgumentError)

Hi, I am a noob in ruby rn. But in the above scenario I am trying to compare i and j to figure out number of occurrence of a alphabet. When I iterate the loop in i and j I am getting the desired output but once I start comparing. It gives an argument error. I have attached images for the reference. Please enlighten me.

CodePudding user response:

It's not the comparison that breaks your program, if you removed it - it still breaks. But if you put some debugging output:

str = "abc".chomp.split("")

i =0  
j = i 1  
l = str.length-1  
for i in str[i..l]  
  k = 0  
  puts "i:#{i.inspect} j:#{j.inspect} k:#{k.inspect}"
  puts "next range (inner loop): #{j}..#{l-1}"
  puts "next range (outer loop): #{i}..#{l}"
  for j in str[j..l-1]  
    puts "j:#{j}"
  end  
end  

you'll see what's wrong:

$ ruby test.rb
i:"a" j:1 k:0
next range (inner loop): 1..1
next range (outer loop): a..2
j:b
i:"b" j:"b" k:0
next range (inner loop): b..1
next range (outer loop): b..2
test.rb:11:in `block in <main>': bad value for range (ArgumentError)
        from test.rb:6:in `each'
        from test.rb:6:in `<main>'

If we focus on the inner loop, before we start it j=1, but after we enter it j becomes the "b" (a string!), and you and second time the loop evaluates the range - it "sees" "b"..2, which makes no sense in the ruby world.

In essence - you're overwriting your variables, try using different variables for the "captures" elements of the array, e.g:

str = "abc".chomp.split("")

i =0  
j = i 1  
l = str.length-1  
for character_outer_loop in str[i..l]  
  k = 0  
  puts "i:#{i.inspect} j:#{j.inspect} k:#{k.inspect}"
  puts "next range (inner loop): #{j}..#{l-1}"
  puts "next range (outer loop): #{i}..#{l}"
  for character_inner_loop in str[j..l-1]  
    puts "j:#{j}"
  end  
end  

CodePudding user response:

The statement of your question is poor but I'm guessing you are trying to compute the number of occurrences of each letter in the string. If so, you could write the following.

def doit(str)
  str.downcase.gsub(/[^a-z]/, '').each_char.tally
end

For example,

str = "Hey, diddle, diddle, the cat and the fiddle.\n"
doit str
  #=> {"h"=>3, "e"=>6, "y"=>1, "d"=>9, "i"=>3, "l"=>3,
  #    "t"=>3, "c"=>1, "a"=>2, "n"=>1, "f"=>1}

The steps are as follows.

s0 = str.downcase
  #=> "hey, diddle, diddle, the cat and the fiddle.\n"

We want "H" in "Hey" and "h" in "the" to be counted as the same letter, so it's easiest to convert all letters to lower case (or to upper case). See String#downcase.

s1 = s0.gsub(/[^a-z]/, '')
  #=> "heydiddlediddlethecatandthefiddle"

Next we remove all characters other than English letters (spaces, punctuation and the newline character at the end of the string). /[^a-z]/ is a regular expression that matches every character that is not a lower case letter. See String#gsub. Notice that there is no need to use String#chomp.

e = s1.each_char
  #=> #<Enumerator: "heydiddlediddlethecatandthefiddle":each_char>

e is an enumerator (an instance of the class Enumerator that generates every character in the string s1. I could have instead written e = s1.chars but that creates an intermediate array that unnecessarily consumes memory. See String#each_char and String#chars.

e.tally
  #=> {"h"=>3, "e"=>6, "y"=>1, "d"=>9, "i"=>3, "l"=>3,
  #    "t"=>3, "c"=>1, "a"=>2, "n"=>1, "f"=>1}

Lastly, we use Enumerable#tally to create a hash that gives the count of each letter in the string.


With regard to the enumerator e = s1.each_char, we see that it has the following behaviour.

e.next #=> "h"
e.next #=> "e" 
e.next #=> "y"
e.next #=> "d"
...
e.next #=> "l"
e.next #=> "e"
e.next #=> StopIteration: iteration reached an end
  • Related