I am attempting to make a console based calculator to practice some Ruby, I am using the 'case' statement to check the consoles arguments then use Ruby's arithmetic to interpret and return the result.
The issue I am having is that I cannot get Ruby to execute the line associated with the Multiplication. It simply goes to the 'else' block and executes my help method. I have all other methods except the Exponent I assume Ruby is treating the STAR characters as 'Everything' (I am not sure how to word that)
case #args[1]
when '+'
puts "#{#args[0].to_i + #args[2].to_i}"
when '-'
puts "#{#args[0].to_i - #args[2].to_i}"
when '*'
puts "#{#args[0].to_i * #args[2].to_i}"
when '/'
puts "#{#args[0].to_i / #args[2].to_i}"
when '%'
puts "#{#args[0].to_i % #args[2].to_i}"
when '**'
puts "#{#args[0].to_i**#args[2].to_i}"
else
help
end
I know it looks dodgy, bare in mind I plan on fixing it up once I have everything working.
I am fairly new to Ruby so I am not 100% sure on what I need to do to have Ruby treat these as simple stars and not Regular Expressions...
I assure you this is not homework, I am simply trying to learn some Ruby as I have also recently started learning Rails.
Did you try escaping the asterisk by writing '\*'?
There's something else going on. This irb session confirms that strings in case statements work the way you expect:
str = '*'
=> "*"
>> case str
>> when '*' then "foo"
>> end
=> "foo"
Double check that #args[1] actually is what you think it is.
Related
I have the following URL:
localhost:3000/filter/shoes/color/white
I need to replace all slashes to - except the first slash from localhost:3000/.
The final URL must be:
localhost:3000/filter-shoes-color-white
I've tried some regex with ruby but I didn't have any success.
Thanks.
Here is a regexp that match all the / but the first:
\G(?:\A[^\/]*\/)?+[^\/]*\K\/
So you can do:
"localhost:3000/filter/shoes/color/white".gsub(/\G(?:\A[^\/]*\/)?+[^\/]*\K\//,'-')
#=> "localhost:3000/filter-shoes-color-white"
But it won't work if you have a scheme on your URI.
TL;DR:
regex is:
\/(?<!localhost:3000\/)
Longer one
A famous old Chinese saying is: Teaching how to fishing is better than giving you the fish.
For regex, you can use online regex site such as regex101.com to test immediately with your regex and test string. link
Found other answers from stackoverflow using other key words to describe your situation: Regex for matching something if it is not preceded by something else
Make you own magic.
This is a pretty simple parsing problem, so I question the need for a regular expression. I think the code would probably be easier to understand and maintain if you just iterated through the characters of the string with a loop like this:
def transform(url)
url = url.dup
slash_count = 0
(0...url.size).each do |i|
if url[i] == '/'
slash_count += 1
url[i] = '-' if slash_count >= 2
end
end
url
end
Here is something even simpler using Ruby's String#gsub method:
def transform2(url)
slash_count = 0
url.gsub('/') do
slash_count += 1
slash_count >= 2 ? '-' : '/'
end
end
Using Ruby >= 2.7 with String#partition
Provided you aren't passing in a URI scheme like 'https://' as part of your string, you can do this as a single method chain with String#partition and String#tr. Using Ruby 3.0.2
'localhost:3000/filter-shoes-color-white'.partition(?/).
map { _1.match?(/^\/$/) ? _1 : _1.tr(?/, ?-) }.join
#=> "localhost:3000/filter-shoes-color-white"
This basically relies on the fact that there are no forward slashes in the first array element returned by #partition, and the second element contains a slash and nothing else. You are then free to use #tr to replace forward slashes with dashes in the final element.
If you have an older Ruby, you'll need a different solution since String#partition wasn't introduced before Ruby 2.6.1. If you don't like using character literals, ternary operators, or numbered block arguments (introduced in Ruby 2.7), then you can refactor the solution to suit your own stylistic tastes.
And another way of doing it. No regex and "localhost" lookback needed.
[url.split("/").take(2).join("/"),url.split("/").drop(2).join("-")].join("-")
I am trying to write a method that takes in a string that will have no spaces. All I want it to do is to return "bug" when the character in the string is a letter [A-Za-z] and to return "ant" if it's any other character.
def Letter(str)
(0..str.length).to_a do |index|
if str[index].chr =~ /[A-Za-z]/ ##I think this is where things are going wrong.
puts "bug"
else
puts "ant"
end
end
end
Does anyone have any idea how to fix this? I keep getting arrays of consecutive numbers.
Rewritten
def letter(str)
str.each_char.map do |char|
(char =~ /[[:alpha:]]/) ? 'bug' : 'ant'
end
end
In your code, you are trying to print "bug" or "ant"; but you're returning (0..str.length).to_a. This function will return an array of bugs and ants. It is also made more Rubyish:
methods should be in snake_case (lowercase, with underscores between words)
iterating over strings is easier with each_char
it's fine with [A-Za-z], but [[:alpha:]] is both clearer and handles Unicode stuff.
since we're testing each character, you know it's going to be one character long, so you don't need the start of line and end of line anchors.
def letter(str)
str.chars.each do |x|
puts x=~ /[A-Za-z]/ ? "bug" : "ant"
end
end
First things first, in your loop your are trying to convert a range
into an array.
(0..str.length).to_a
str.length returns a number, therefore making it into an array will give you an array of numbers. Hence your problem.
Second, you have to have brackets around your /a-zA-Z/ regex
Third, use the ternary operator. It's great for small if statements. Heres the syntax:
boolean ? "if boolean is true this code will execute" : "else this code will"
Fourth, use the .each methods, ruby is loved partly because of the simplicity of loops and iterating!
Happy coding!
I have the following code. However I get a error. How is this supposed to be written.
puts 'What is your favourite number?'
number = gets.chomp
number = number.to_i + 1
puts "you would like " + number + 'much better'
It always helps if you include the error. There are two ways to fix that error:
Interpolate the value: puts "you would like #{number} much better"
Turn it from a number to a string: puts "you would like " + number.to_s + 'much better'
The former, #{...} syntax, evaluates the content of the braces as Ruby, and then applies to_s to the result, before injecting it into the string. My two examples are literally equivalent.
As to why it fails? + doesn't do type coercion in Ruby, which actually has very little implicit conversion going on, unlike other languages in similar spaces.
Is there and way to write code like this in a way that makes what it does clearer?
a = (a.split(" ")[1..-1]).join(" ")
That deletes the first word of a sentence but the code doesn't look expressive at all.
irb(main):024:0> "this is a test".split.drop(1) * " "
=> "is a test"
Edited to add:
Explanation:
By default #split delimits on whitespace.
#drop(1) gets rid of the first entry.
* " " does the same as #join(" ").
for somebody who is used to reading rexep this is pretty clean:
a = a.sub(/^\S+\s/, "")
ymmv
code
a = a.split[1..-1] * " "
explanation
String#split's default parameter is " "
Array * String is an alias for Array.join(String)
On second thought, I'm not sure if it's more transparent to someone who is not familiar with ruby, per se. But anyone who has worked with Ruby strings for a little bit will understand what's going on. And it's a lot more clean than the original version.
UPDATE
As per just-my-correct-opinion's answer (which you all should vote up instead of mine), if you are running Ruby 1.9.1 (which you should be, anyway) or Ruby 1.8.7, you can do:
a = a.split.drop(1) * " "
maybe making the process explicit will help
words = a.split
words.pop
a = words.join " "
And if you were using this throughout some code you might want to create the
methods in String and Array to make your code readable. (works in 1.8.6 too)
class String
def words
split
end
end
class Array
def but_first
self[1..-1]
end
def to_sentence
join(' ')
end
end
str = "And, this is a sentence"
puts str.words.but_first.to_sentence
Something like following
a = a[a.index(' '), a.length-(a.index(' ')+1)]
No check though
Try the following code:
s = '#value#'
puts s.gsub('#value#', Regexp.escape('*')) # => '\*'
puts s.gsub('#value#', Regexp.escape('+')) # => ''
Wtf? It looks like the char '\+' (returned by Regexp.escape) is completely ignored by gsub. How to fix this?
It's because of the interpolation of special variables. \+ will be replaced with "the text matched by the highest-numbered capturing group that actually participated in the match" (See the Special Variables section on http://www.regular-expressions.info/ruby.html)
The block syntax is in fact a fix for this, well done.
xsdg of #ruby worked this out
Looks like that gsub's replacement is parsed, so the + is lost somewhere in the process. A workaround is using gsub's block syntax. This way:
s = '#value#'
puts s.gsub('#value#') { |v| Regexp.escape('+') } # => '+'
Works as expected :)
Thanks, xsdg!