I get the text containing the sum from an element:
sum = browser.div(:class => 'single sort', :index => 0).div(:class, 'amount').text
Then I have to compare sum with another integer, but sum is still a string:
>> sum = browser.div(:class => 'single sort', :index => 0).div(:class, 'amount').text
=> "7 671 \u0420"
>> puts sum.class
String
>> sum.gsub!(/[^0-9]/, '')
=> "7671"
>> puts sum.class
String
>> sum.to_i
=> 7671
>> puts sum.class
String
How can I convert sum to an integer?
You still have to assign the new value to sum:
sum = sum.to_i
You have to override the results in this way:
sum = sum.to_i
I suggest to use Integer(sum) instead of .to_i method to raise an exception if you try to convert a not number string.
nil.to_i = 0
"".to_i = 0
Integer("")
ArgumentError: invalid value for Integer(): ""
from (irb):1:in `Integer'
from (irb):1
from /Users/nico/.rvm/rubies/ruby-2.2.0/bin/irb:11:in `<main>'
String#to_i returns a new Object (which is the only sensible implementation; think a moment about what would happen if String#to_i magically changed the object's class).
So, to assign the integer value to your sum variable, use
sum = sum.to_i
or (better) introduce a new variable for your integer value:
sum_as_integer = sum.to_i
Related
I'm doing a simple calculator using Ruby as practice. Everything is fine except how do I identify if a character/symbol I input is a number or not using if-else statement.
For example:
Enter first number: a
Error: Enter correct number
Enter first number: -
Error: Enter correct number
Enter first number: 1
Enter second number:b
Error: Enter correct number
Enter second number: 2
Choose operator (+-*/): *
The product is: 2
This is the code I input first:
print "Enter first number: "
x = gets.to_i
print "Enter second number: "
y = gets.to_i
print "Choose operator (+-*/): "
op = gets.chomp.to_s
I will use if-else statement to identify if the number input is a number or not
If you wish to test if the string represents an integer or float use Kernel#Integer or Kernel#Float with the optional second argument (a hash) having the value of the key :exception equal to false.
For example,
Integer('-123', exception: false)
#=> -123
Integer("0xAa", exception: false)
#=> 170
Integer('12.3', exception: false)
#=> nil,
Integer('12a3', exception: false)
#=> nil
Float('123', exception: false)
#=> 123.0
Float("1.2e3", exception: false)
#=> 1200.0
Float('12a3', exception: false)
#=> nil,
Float('1.2.3', exception: false)
#=> nil
Note
Integer("123\n", exception: false)
#=> 123
shows you don't have to chomp before testing if the string represents an integer (similar with Float).
Some of the examples illustrate a limitation or complication when using a regular expression to test whether a string represents an integer or float.
Create a new String method
irb(main):001:0> class String
irb(main):002:1> def number?
irb(main):003:2> Float(self) != nil rescue false
irb(main):004:2> end
irb(main):005:1> end
irb(main):012:0> x = gets
1
=> 1
irb(main):013:0> x.number?
=> true
irb(main):009:0> x = gets
s
=> "s\n"
irb(main):010:0> x.number?
=> false
This typical way to do this would be to match it with a regexp:
x = gets.chomp
if x.match?(/^\d+$/)
puts "#{x} is a number"
else
puts "#{x} is not a number"
end
This tests if the given string matches the pattern ^\d+$, which means "start of line, one or more digit characters, then end of line". Note that this won't match characters like "," or "." - only a string of the digits 0-9. String#match? will return a boolean indicating if the string matches the given pattern or not.
use this ruby method
.is_a?(Integer)
For example:
input = gets.chomp
input.is_a?(Integer) # => true || false
I am doing a coding exercise(just to clear up any questions beforehand). My objective is to be able to offset the hash key by a specified amount. The problem I am having is if the hash key is a symbol. My approach is to turn it into a string and go from there. Here is my code:
class :: Hash
def transpose_key(offset)
self.each_key{|key|
t = (key.to_s.ord - "a".ord + offset)
key = (t % 26) + "a".ord.chr
}
return self
end #def
end #class
wrong_keys = { :a => "rope", :b => "knife", :x => "life-jacket", :z => "raft" }
puts wrong_keys.transpose_key(2)
I am getting the following error:
test.rb:6:in `+': String can't be coerced into Fixnum (TypeError)
I'm confused because I would think (key.to_s.ord) would give me a string letter on which to convert to ascii? I will later add functionality for numbered keys. Most of all I would like to, if at possible, use the approach I have started and make it work. Any ideas?
UPDATED
Here is my new code:
def transpose(string, offset)
#string = string.chars
string.each_codepoint {|c| puts (c + offset) > 122 ? (((c - 97) + offset) % 26 + 97).chr : (c + offset).chr}
end
transpose('xyz', 5)
...the output is correct, but it puts every character on different line. I have tried a various ways to try to join it, but can't seem to. If I use print in the iteration instead of puts, the output is joined, but I don't get a new line, which I want. Why is that and how can I fix it?
I'm confused because I would think (key.to_s.ord) would ...
That's the wrong line.
The next line is the line raising the error, and you're not doing .to_s.ord, you're doing .ord.to_s:
key = (t % 26) + "a".ord.chr
"a".ord.chr has no meaning, you're converting a character to an ordinal and back to a character, and then trying to add an integer and a character, hence your error. Replace "a".ord.chr with "a".ord
If I understand your question correctly, I think this gives you want you want:
class Hash
def transpose_key(offset)
map do |key, value|
t = (key.to_s.ord - "a".ord + offset) % 26
[(t + "a".ord).chr.to_sym, value]
end.to_h
end
end
wrong_keys = { :a => "rope", :b => "knife", :x => "life-jacket", :z => "raft" }
puts wrong_keys.transpose_key(2)
# {:c=>"rope", :d=>"knife", :z=>"life-jacket", :b=>"raft"}
Array#to_h (v2.0+) is an alternative to the class method Hash::[] (v1.0+)for converting an array of two-element arrays to a hash:
a = [[1,2],[3,4]]
Hash[a] #=> {1=>2, 3=>4}
a.to_h #=> {1=>2, 3=>4}
If we removed .to_h from the method we would find that the value returned by map (to which to_h is applied) is:
[[:c, "rope"], [:d, "knife"], [:z, "life-jacket"], [:b, "raft"]]
To use Hash#each_key, you could do this:
class Hash
def transpose_key(offset)
each_key.with_object({}) do |key,h|
t = (key.to_s.ord - "a".ord + offset) % 26
h[(t + "a".ord).chr.to_sym] = self[key]
end
end
end
puts wrong_keys.transpose_key(2)
# {:c=>"rope", :d=>"knife", :z=>"life-jacket", :b=>"raft"}
On reflection, I prefer the latter method.
I get TypeError: no implicit conversion of String into Integer Couldn't figure out what is wrong here.
require 'json'
h = '{"name":[{"first":"first ", "last":"last"}], "age":2}'
h = JSON.parse(h)
class C
def fullname(p)
first(p["name"]) + last(p["name"])
end
def age(p)
p["age"]
end
private
def first(name)
name["first"]
end
def last(name)
name["last"]
end
end
C.new.age(h) #=> 2
C.new.fullname(h) #=> TypeError: no implicit conversion of String into Integer
Name is an array, you have two options:
Option A:
Give fullname an element of the array:
def fullname(elem)
first(elem) + last(elem)
end
And call it with
C.fullname(p.first)
for instance
Option B:
Assume that it's always the first element of the array in fullname
def fullname(p)
name=p["name"].first
first(name) + last(name)
end
Don't be confused by Array.first which is Array[0] and your 'first' function
The result of h["name"] is name = [{"first" => "first ", "last" => "last"}], which is an array. You cannot apply name["first"] or name["last"]. The argument passed to an array has to be an integer.
"name" is an Array. fullname(p) should read
first(p["name"][0]) + last(p["name"][0])
I am writing a TFIDF program - all of which should be okay, but I'm having a small (or large..) problem with the hashes working as intended.
To keep this short, the code at hand is:
#Word matrix is an array that contains hashes (obviously)
#i've done some stuff before this and these are working as expected
puts word_matrix[3][:yahoo] # => 2
puts word_matrix[100][:yahoo] # => 0
puts $total_words_hash[:yahoo] #=> 0
#Essentially, this block is taking a hash of all the words (values = 0) and trying
#to run through them adding the only the values of the other hash to the temporary
#and then setting the temp to the old hash position (so that there are 0 values
#and the values occurring in that document.. yet, it assigns the same values to
#ALL of the hashes of word_matrix[]
#now we run this block and everything breaks down for some reason..
for i in 0...word_matrix.size
tmp_complete_words_hash = $total_words_hash #all values should be zero...
word_matrix[i].each do |key,val| #for each key in the hash we do this..
tmp_complete_words_hash[key] = val
end
word_matrix[i] = tmp_complete_words_hash
end
puts word_matrix[3][:yahoo] # => 2
puts word_matrix[100][:yahoo] # => 2 -- THIS SHOULD BE 0 Still...
Could anyone shed any light as to why this is assigning the same values to ALL the hashes of the array? It is as if tmp_complete_words_hash is not being reset everytime.
You need to clone the hash.
tmp_complete_words_hash = $total_words_hash.clone
Otherwise, both variables are pointing to the same hash, and you're constantly modifying that hash.
In fact, most objects in Ruby are like this. Only a few (such as numerics, strings) aren't.
Try this in the IRB:
class MyClass
attr_accessor :value
end
x = MyClass.new
y = x
x.value = "OK"
puts y.value
why this is assigning the same values to ALL the hashes of the array?
There is only one hash. You are assigning the same hash (the one pointed to by $total_words_hash) to every element in the array:
tmp_complete_words_hash = $total_words_hash
Here, you make tmp_complete_words_hash point to the same object as $total_words_hash
word_matrix[i] = tmp_complete_words_hash
And here you assign that hash to every element of the array.
When you assign a hash variable to another hash variable. It will reference the same memory location, if you change one hash, same will be reflected to another hash.
total_words_hash = {}
tmp_complete_words_hash = total_words_hash
1.9.3 (main):0 > total_words_hash.object_id
=> 85149660
1.9.3 (main):0 > tmp_complete_words_hash.object_id
=> 85149660
total_words_hash[:test] = 0
1.9.3 (main):0 > tmp_complete_words_hash
=> {
:test => 0
}
1.9.3 (main):0 > tmp_complete_words_hash[:test_reverse] = 1
=> 1
1.9.3 (main):0 > tmp_complete_words_hash
=> {
:test => 0,
:test_reverse => 1
}
So you can create a duplicate hash for this purpose using hash method dup.
1.9.3 (main):0 > tmp_complete_words_hash = total_words_hash.dup
1.9.3 (main):0 > total_words_hash.object_id
=> 85149660
1.9.3 (main):0 > tmp_complete_words_hash.object_id
=> 97244920
In your case just use.
tmp_complete_words_hash = $total_words_hash.dup
Why does ruby addition cannot coerce given string to fixnum and vice verca?
irb>fixnum = 1
=> 1
irb> fixnum.class
=> Fixnum
irb> string = "3"
=> "3"
irb> string.class
=> String
irb> string.to_i
=> 3
irb> fixnum + string
TypeError: String can't be coerced into Fixnum
from (irb):96:in `+'
from (irb):96
from :0
irb(main):097:0>
Because ruby doesn't know whether you want to convert the string to int or the int to string. In e.g. java 1 + "3" is "13". Ruby prefers to raise an error, so that the user can decide whether to to_s the one operand or to_i the other, rather than doing the wrong thing automatically.
It spits out an error because ruby doesn't know what you want. String + fixnum is an ambiguous statement. It could either mean that you want to add the int value of the string and the number, or the string value of the int and the string. For example:
str = "5"
num = 3
puts str.to_i + num
=> 8
puts str + num.to_s
=> 53
Rather than taking a guess as to which of those you want, ruby just throws an exception.
You need to set the variable back to itself, since doing a .to_i returns a integer value, but doesn't modify the original object.
>> string = "3"
=> "3"
>> string.class
=> String
>> string.to_i
=> 3
>> string
=> "3"
>> string.class
=> String
>> string = string.to_i
=> 3
>> string.class
=> Fixnum
Fixnum#+ is just a method. Simplified it works like this:
class Fixnum
def +(other)
if Fixnum === other
# normal operation
else
n1, n2= other.coerce(self)
return n1+n2
end
end
end
coerce is used for automatic numerical type conversion. This is used for example if you do 42 + 3.141. A string in ruby cannot automaticly be converted into numerical values. You could enhance the String class to be able to do this. You just need to do
class String
def coerce(other)
coerced= case other
when Integer
self.to_i
when
self.to_f
end
return coerced, other
end
end
Now you can do
23 + "42" # => 65
0.859 - "4" # => 3.141
This does not work the other way arround. coerce is only for numerical values "23" + 42 won't work. String#+ will not use coerce.
The simplified + is done in Fixnum and not in Integer on purpose. Fixnum and Bignum have their separate methods, because they work internally very different.