I'm getting an unexpected keyword error for "elsif" - ruby

def letter_case_count(string)
char = new Hash
char[:lower] = 0
char[:upper] = 0
char[:neither] = 0
string.split("").each do |x|
if ('A'..'Z').include?(x)
char[:upper]++
elsif ('a'..'z').include?(x)
char[:lower]++
else
char[:neither]++
end
end
end
puts letter_case_count('abCdef 123') == { lowercase: 5, uppercase: 1, neither: 4 }
puts letter_case_count('AbCd +Ef') == { lowercase: 3, uppercase: 3, neither: 2 }
puts letter_case_count('123') == { lowercase: 0, uppercase: 0, neither: 3 }
puts letter_case_count('') == { lowercase: 0, uppercase: 0, neither: 0 }
I get this error.
(repl):9: syntax error, unexpected keyword_elsif
elsif ('a'..'z').include?(x)
^
(repl):11: syntax error, unexpected keyword_else
(repl):13: syntax error, unexpected keyword_end
(repl):20: syntax error, unexpected end-of-input, expecting keyword_end
...: 0, uppercase: 0, neither: 0 }
...
^
There are a bunch of unexpected keywords and unexpected end of inputs. Not sure why, I haven't programmed in Ruby in almost a yearn and I can't see what the problem is.

Ruby doesn't have pre-increment or post-increment operators. Use +=1 and it should work.
def letter_case_count(string)
char = {}
char[:lower] = 0
char[:upper] = 0
char[:neither] = 0
string.split('').each do |x|
if ('A'..'Z').include?(x)
char[:upper] += 1
elsif ('a'..'z').include?(x)
char[:lower] += 1
else
char[:neither] += 1
end
end
char #returning the char is also important.
end
Using different keys for comparison will also return wrong results, it should be..
puts letter_case_count('abCdef 123') == { lower: 5, upper: 1, neither: 4 }
puts letter_case_count('AbCd +Ef') == { lower: 3, upper: 3, neither: 2 }
puts letter_case_count('123') == { lower: 0, upper: 0, neither: 3 }
puts letter_case_count('') == { lower: 0, upper: 0, neither: 0 }
Better Approach:
def letter_case_count(string)
{
lower: string.scan(/[a-z]/).count,
upper: string.scan(/[A-Z]/).count,
neither: string.scan(/[^a-z]/i).count
}
end

There are bunch of syntax errors in your code.
def letter_case_count(string)
char = Hash.new # not new Hash
char[:lower] = 0
char[:upper] = 0
char[:neither] = 0
string.split("").each do |x|
if ('A'..'Z').include?(x)
char[:upper]+=1 # var++ is not valid ruby code
elsif ('a'..'z').include?(x)
char[:lower]+=1 #same here
else
char[:neither]+=1 # same here
end
end
end
puts letter_case_count('abCdef 123') == { lowercase: 5, uppercase: 1, neither: 4 }
puts letter_case_count('AbCd +Ef') == { lowercase: 3, uppercase: 3, neither: 2 }
puts letter_case_count('123') == { lowercase: 0, uppercase: 0, neither: 3 }
puts letter_case_count('') == { lowercase: 0, uppercase: 0, neither: 0 }
UPDATE
For that kind of tasks, use Ruby's minitest from stdlib.
All in one file example (all of them will fail)
require 'minitest/autorun'
class String
def letter_case_count
char = Hash.new # not new Hash
char[:lower] = 0
char[:upper] = 0
char[:neither] = 0
self.split("").each do |x|
if ('A'..'Z').include?(x)
char[:upper]+=1 # var++ is not valid ruby code
elsif ('a'..'z').include?(x)
char[:lower]+=1 #same here
else
char[:neither]+=1 # same here
end
end
return char
end
end
class TestFoo < MiniTest::Test
def setup
#w1, #w2, #w3, #w4 = ["abCdef 123", "AbCd +Ef", "123", ""].map {|e| String.new(e)}
end
def test_some
assert_equal #w1.letter_case_count, { lowercase: 3, uppercase: 1, neither: 4 }
end
def test_some_other
assert_equal #w2.letter_case_count, { lowercase: 3, uppercase: 3, neither: 2 }
end
def test_other
assert_equal #w3.letter_case_count, { lowercase: 0, uppercase: 0, neither: 3 }
end
def test_definitely_other
assert_equal #w4.letter_case_count, { lowercase: 0, uppercase: 0, neither: 0 }
end
end

Related

Hash tree depth Ruby

I'm supposed to write a method that takes a nested hash as input and returns that hash with added "depth" keys. So, for example, the following input:
tree = {
a: 1,
b: 2,
c: { d: { e: 3 } }
}
would yield the following return value:
{
a: 1,
b: 2,
c: {
d: {
e: 3,
depth: 2
},
depth: 1
},
depth: 0
}
If the input is not a hash, then the function should return nil.
This is what I came up with:
def depth(hash)
num = 0
hash.each do |key, value|
if value.class == Hash
num += 1
v[:depth] = num
value.each do |k, v|
if v.class == Hash
num += 1
v[:depth] = num
v.each do |ky, val|
if val.class == Hash
num += 1
v[:depth] = num
val.each do |ke, vl|
if vl.class == Hash
num += 1
v[:depth] = num
end
end
end
end
end
end
end
num = 0
end
end
but it's limited to hash depth of 4, and I can't just keep making the method bigger.
Try this.
def depth(h, i=0)
h.each_with_object(depth: i) { |(k,v),g| g[k] = v.is_a?(Hash) ? depth(v, i+1) : v }
end
depth { a: 1, b: 2, c: { d: { e: 3 } }
#=> {:depth=>0, :a=>1, :b=>2, :c=>{:depth=>1, :d=>{:depth=>2, :e=>3}}}

How to return "invalid" instead a sum of an array when input is a string or a float

I want to return an array of sums of multiples of 3 and 5 from 0 to n. And I want to return "invalid" when the input is a string, a float or < 0
def is_multiple_of_3_or_5(n)
if n.class == Integer && n > 0
n % 3 == 0 || n % 5 == 0 ? true : false
else
puts "invalid"
end
end
def sum_of_3_and_5_multiples(n)
if n.class == Integer
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
array_of_multiples_of_3_and_5.inject(0, :+)
end
end
sum_of_3_and_5_multiples(-1)
To get the sums of multiples of 3 and 5 I got this but when I try with -1 that return me 0 instead "invalid", with"string"` that return me an error.
You havent' put any code in your sum_of_3_and_5_multiples method to handle what happens if is_multiple_of_3_or_5 is invalid (or to put it another way, a string). You also don't need to puts 'invalid', as this returns a value of null. Just 'invalid' will do:
def is_multiple_of_3_or_5(n)
if n.class == Integer && n > 0
n % 3 == 0 || n % 5 == 0 ? true : false
else
"invalid"
end
end
def sum_of_3_and_5_multiples(n)
if n.class == Integer
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
return "invalid" if is_multiple_of_3_or_5(i).is_a?(String)
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
array_of_multiples_of_3_and_5.inject(0, :+)
end
end
sum_of_3_and_5_multiples(-1)
=> "invalid"
One could do that as follows.
def sum_of_3_and_5_multiples(n)
case n
when Float, String, -Float::INFINITY...0
return 'invalid'
end
((0..n).step(3).to_a + (0..n).step(5).to_a).uniq
end
sum_of_3_and_5_multiples(11.5)
#=> "invalid"
sum_of_3_and_5_multiples("11")
#=> "invalid"
sum_of_3_and_5_multiples(-340)
#=> "invalid"
sum_of_3_and_5_multiples(15)
#=> [0, 3, 6, 9, 12, 15, 5, 10]
sum_of_3_and_5_multiples(87)
#=> [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45,
# 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87,
# 5, 10, 20, 25, 35, 40, 50, 55, 65, 70, 80, 85]
Alternative verbose option, using a monkey patch to String class and a custom Class, stealing from Cary's answer (https://stackoverflow.com/a/59876202/5239030) but with a three dot Range for excluding the extreme value.
Patching the String class for using methods like this Numeric#integer? and Numeric#positive?. I'd suggest to use Kernel#raise in case of error.
module MyStringPatch
def integer?
false
end
def positive?
false
end
end
String.include MyStringPatch
Writing the custom class
class MyNumber
def initialize(n)
raise 'Invalid' unless n.integer? && n.positive?
#n = n
end
def sum_of_3_and_5_multiples
(((0...#n).step(3).to_a + (0...#n).step(5).to_a).uniq).sum
end
end
Finally using it
n = 32
my_number = MyNumber.new(n)
p my_number.sum_of_3_and_5_multiples
#=> 225
Or ...in initialize': Invalid (RuntimeError) in case of n = "32" or n = -32 or n = 32.0.
You can use something like:
return "invalid" unless n.is_a? Integer || n.positive?
Taking a look at: https://rubystyle.guide/ may help
I've found this ! that worked !
def is_multiple_of_3_or_5(n)
n % 3 == 0 || n % 5 == 0 || n == 0 ? true : false
end
def sum_of_3_and_5_multiples(n)
puts n.class
if n.class == Integer && n >= 0
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
return array_of_multiples_of_3_and_5.inject(0, :+)
end
if n.class != Integer || n < 0
return "invalid"
end
end
thanks for your help that was helpful !

Calculating daily lag

Is there a nicer way to calculate lag (value - previous value) in ruby? This is what I came up with. If the lag is greater than 0 I return 1, if it is less I return -1
(my code below does not work. Using ruby 2.3.5)
def lagChange(arr,k=1)
prev=nil
ret=[]
arr.each{|x|
if(x.to_i>-9999)
ret<<if(x-prev>0){1}else{-1}end
else
ret<<"NA"
end
prev=x.to_i
}
return ret
end
lagChange([1,2,3,5]) #NA 1 1 -1
if(x-prev>0){1}else{-1}end is not valid Ruby.
def lagChange(arr,k=1)
prev = nil
ret = []
arr.each{|x|
if prev == nil
ret << "NA"
elsif(x.to_i>-9999)
ret << (x-prev > 0 ? 1 : -1)
end
prev=x.to_i
}
return ret
end
lagChange([1,2,3,1])
=> ["NA", 1, 1, -1]
A shorter version could be
def lag_change(arr)
arr.each_cons(2).map {|x,y| y-x > 0 ? 1 : -1}.unshift('NA')
end
> lag_change([1,2,3,1])
=> ["NA", 1, 1, -1]
Looks like the spaceship operator might come in handy here:
def lagChange(arr)
arr.map.with_index do |x, idx|
idx == 0 ? "NA" : x <=> arr[idx - 1]
end
end
lagChange([1,2,2,1])
=> ["NA", 1, 0, -1]
Note that this also returns 0 if they are equal(no lag).

Why doesn't the ternary syntax work in this situation?

This is valid:
def prime? n
(2...n).each {|num| return false if n % num == 0 }
true
end
This is NOT valid
def prime? n
(2...n).each {|num| n % num == 0 ? return false : true }
end
Aren't these both saying the same thing? Why does the second example cause a syntax error?
Now why does this next example work in the ternary syntax?
def primes max
primes_arr = []
(2...max).map { |num| prime?(num) ? primes_arr << num : next }
primes_arr
end
That code cause a syntax error because the ruby interpreter don't know, where the arguments for return keyword has end up, the correct example is:
def prime? n
(2...n).each {|num| n % num == 0 ? ( return false ) : true }
end
Here is the next isn't really required just use boolean logic:
def primes max
primes_arr = []
(2...max).map { |num| prime?(num) && primes_arr << num }
primes_arr
end
If we do some optimization, we can do:
def prime? n
(2...n).all? {|num| n % num != 0 }
end
def primes max
(2...max).select {|num| prime?(num) }
end
Next level of optimization shell be this:
def primes max
(2...max).select {|num| (2...num).all? {|num1| num % num1 != 0 } }
end
primes 7
# => [2, 3, 5]
Adding parenthesis to explicitly state the way Ruby is interpreting what you have:
(2...n).each { |num| (n % num == 0 ? return false) : true }
We can verify this in that we get the same error message with and without the parenthesis:
SyntaxError: (eval):2: syntax error, unexpected keyword_false, expecting ':'
You can solve this by giving explicit parenthesis to force Ruby to interpret it how you want:
(2...n).each { |num| n % num == 0 ? (return false) : true }
Or you can only return false when the case is met (as you have in your question). Or, better yet, use any? instead:
(2...n).any? { |num| n % num != 0 }
Here's a proper usage of ternary operator (no operator at all :) )
def prime? n
!(2...n).any? {|num| n % num == 0 }
# or
# (2...n).all? {|num| n % num != 0 }
end
(1..10).each do |x|
puts "prime?(#{x}) \# => #{prime?(x)}"
end
# >> prime?(1) # => true
# >> prime?(2) # => true
# >> prime?(3) # => true
# >> prime?(4) # => false
# >> prime?(5) # => true
# >> prime?(6) # => false
# >> prime?(7) # => true
# >> prime?(8) # => false
# >> prime?(9) # => false
# >> prime?(10) # => false

Cycle through elements of an array

I would like to cycle #a from 0 through 2: 0, 1, 2, 0, 1, 2.
def set_a
if #a == 2
#a = 0
else
#a = #a + 1
end
end
Maybe there is a better way?
(0..2).cycle(3) { |x| puts x } #=> 0,1,2,0,1,2,0,1,2
item = [0, 1, 2].cycle.each
item.next #=> 0
item.next #=> 1
item.next #=> 2
item.next #=> 0
...

Resources