Why is this string value converted to an array? - ruby

I have the following function:
def parse_var(var)
value = instance_variable_get(var)
puts(value)
puts(value.to_s)
value.is_a?(Numeric) ? value.to_s : "\"#{value}\""
end
Variables of certain form are converted into an array when they are interpolated. In the above function, when value equals (684) 029-6183 x01024, value.to_s comes out to be ["(684) 029-6183 x01024", nil]. The same thing also happens when I try "#{value}".
What is causing this?
Here's the context of the code in question:
Entity.rb (context of above code)
require 'securerandom'
# Entity.rb
class Entity
def initialize
generate_uuid
end
def to_cypher
first_char = self.class.name.chr.downcase
"MERGE (#{first_char}:#{self.class.name} {#{attrs_to_cypher.join(', ')}}) RETURN #{first_char};"
end
protected
def rand_bool
[true, false].sample
end
private
def attrs_to_cypher
self.instance_variables.map do |var|
"#{camelize(var.to_s[1..-1])}:#{parse_var(var)}"
end
end
def generate_uuid
#uuid = SecureRandom.uuid
end
def parse_var(var)
value = instance_variable_get(var)
puts(value)
puts(value.to_s)
value.is_a?(Numeric) ? value.to_s : "\"#{value}\""
end
def camelize(s)
(s == "uuid") ? "UUID" : s.downcase.split('_').map(&:capitalize).join
end
end
PhoneNumber.rb (where the value is coming from)
require 'faker'
require_relative 'Entity'
# PhoneNumber.rb
class PhoneNumber < Entity
def initialize(**opts)
super()
#type = opts[:type] || rand_bool ? "cell" : "home"
#number = opts[:number] || #type == "cell" ? Faker::PhoneNumber.cell_phone : Faker::PhoneNumber.phone_number,
#country_code = opts[:country_code] || nil
#area_code = opts[:area_code] || nil
end
end

The following line of code is causing a couple of issues
#number = opts[:number] || #type == "cell" ? Faker::PhoneNumber.cell_phone : Faker::PhoneNumber.phone_number,
First, the || operator has a higher precedence than the ? operator, so it actually looks like:
#number = (opts[:number] || #type == "cell") ? Faker::PhoneNumber.cell_phone : Faker::PhoneNumber.phone_number,
and you probably were wanting this:
#number = opts[:number] || (#type == "cell" ? Faker::PhoneNumber.cell_phone : Faker::PhoneNumber.phone_number),
As it stands, it doesn't matter what you pass into opts[:number], you'll always get a Faker::PhoneNumber assigned. (The line above, assigning type, looks like it would have this same precedence issue)
Second, you have a stray comma at the end of the line. This is turning the entire line into the first element of an array, and doesn't interfere with assigning the variable on the next line, so it's hard to catch:
opts = { number: '123' }
type = "cell"
number = opts[:number] || type == "cell" ? "truthy" : "falsey",
country = "some value"
p number # => ["truthy", "some value"]
p country # => "some value"

Your variable contains ["(684) 029-6183 x01024", nil], not a string. You rely on puts(value) output to determine it’s value which is plain wrong:
puts ["(684) 029-6183 x01024", nil]
#⇒ (684) 029-6183 x01024
#
puts(nil) returns an empty string, making you think the original value is a string.
To overcome this issue you might:
value = [*value].compact.join

Related

ruby - access array with hash key

I am struggling to understand how I can access an array with a hash key. In my code, I create a hash with keys and values. Now, I want to set the values in a Car class. Whenever I try to instantiate the Car, the argument expects Integer and not a String.
I am getting the following error: TypeError (no implicit conversion of String into Integer)
Here is my code:
class Car_maker
attr_accessor :car_maker
def initialize(car_maker)
#car_maker = car_maker
end
end
class Car_model < Car_maker
attr_accessor :km, :type, :transmission, :stock, :drivetrain, :status,
:fuel, :car_maker, :model, :year, :trim, :features
#total number of instances & array with car objects
##totalCars = 0
##catalogue = []
def initialize(km, type, transmission, stock, drivetrain, status, fuel, car_maker, model, year, trim, features)
super(car_maker)
#km = km
#type = type
#transmission = transmission
#stock = stock
#drivetrain = drivetrain
#status = status
#fuel = fuel
#model = model
#year = year
#trim = trim
#features = features
##totalCars += 1
end
def self.convertListings2Catalogue(line)
#Initialise arrays and use them to compare
type = ["Sedan", "coupe", "hatchback", "station", "SUV"]
transmission = ["auto", "manual", "steptronic"]
drivetrain = ["FWD", "RWD", "AWD"]
status = ["new", "used"]
car_maker = ["honda", "toyota", "mercedes", "bmw", "lexus"]
hash = Hash.new
#In this part, we hash the set of features using regex
copyOfLine = line
regex = Regexp.new(/{(.*?)}/)
match_array = copyOfLine.scan(regex)
match_array.each do |line|
hash["features"] = line
end
#Now, we split every comma and start matching fields
newStr = line[0...line.index('{')] + line[line.index('}')+1...line.length]
arrayOfElements = newStr.split(',')
arrayOfElements.each do |value|
if value.include?("km") and !value.include?("/")
hash["km"] = value
elsif type.include?(value)
hash["type"] = value
elsif transmission.include?(value.downcase)
hash["transmission"] = value
elsif value.include?("/") and value.include?("km")
hash["fuel economy"] = value
elsif drivetrain.include?(value)
hash["drivetrain"] = value
elsif status.include?(value.downcase)
hash["status"] = value
elsif /(?=.*[a-zA-Z])(?=.*[0-9])/.match(value) and !value.include?("km")
hash["stock"] = value
elsif car_maker.include?(value.downcase)
hash["carmaker"] = value
elsif /^\d{4}$/.match(value)
hash["year"] = value
elsif value.length == 2
hash["trim"] = value
else
if value.length > 2
hash["model"] = value
end
end
end
end
end
textFile = File.open('cars.txt', 'r')
textFile.each_line{|line|
if line.length > 2
result = Car_model.convertListings2Catalogue(line)
puts "Hash: #{result}"
carObj = Car_model.new(result["km"], result["type"], result["transmission"], result["stock"], result["drivetrain"],
result["status"], result["fuel"], result["carmaker"], result["model"], result["year"], result["trim"], result["features"])
###catalogue.push (carObj)
end
}
This line
result = Car_model.convertListings2Catalogue(line)
Doesn't return the hash object. It returns arrayOfElements since that's what the each method actually returns and the each method is the last method executed in the method (although there are hash assignments within it, it's only the last value that's returned unless you use an explicit return statement.
Just use the variable hash in the last line of the convertListing2Catalog method
if value.length > 2
hash["model"] = value
end
end
end
hash # < this is the last line of the method so it's the value that will be returned
end
end
If you think about it, there were several variables created in the method. There's no reason to expect that the contents of any specific variable such as hash would be returned, and ruby methods by default return the last executed command.

ruby trie implementation reference issue

I am trying to implement a trie in Ruby but can't figure out what the problem is with my print + collect methods.
I just implemented the same in JS and working fine. I guess the issue could be that Ruby is passed by reference (unlike JS) and how variable assignment works in Ruby.
So if I run the code with string.clone as argument when I recursively call the collect function then I get:
["peter", "peter", "petera", "pdanny", "pdjane", "pdjanck"]
and if I pass string then:
["peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck"]
Any ideas how to fix this?
the code:
class Node
attr_accessor :hash, :end_node, :data
def initialize
#hash = {}
#end_node = false
#data = data
end
def end_node?
end_node
end
end
class Trie
def initialize
#root = Node.new
#words = []
end
def add(input, data, node = #root)
if input.empty?
node.data = data
node.end_node = true
elsif node.hash.keys.include?(input[0])
add(input[1..-1], data, node.hash[input[0]])
else
node.hash[input[0]] = Node.new
add(input[1..-1], data, node.hash[input[0]])
end
end
def print(node = #root)
collect(node, '')
#words
end
private
def collect(node, string)
if node.hash.size > 0
for letter in node.hash.keys
string = string.concat(letter)
collect(node.hash[letter], string.clone)
end
#words << string if node.end_node?
else
string.length > 0 ? #words << string : nil
end
end
end
trie = Trie.new
trie.add('peter', date: '1988-02-26')
trie.add('petra', date: '1977-02-12')
trie.add('danny', date: '1998-04-21')
trie.add('jane', date: '1985-05-08')
trie.add('jack', date: '1994-11-04')
trie.add('pete', date: '1977-12-18')
print trie.print
Ruby's string concat mutates the string and doesn't return a new string. You may want the + operator instead. So basically change the 2 lines inside collect's for-loop as per below:
stringn = string + letter
collect(node.hash[letter], stringn)
Also, you probably want to either always initialize #words to empty in print before calling collect, or make it a local variable in print and pass it to collect.

what is the purpose of ".push" in this line "#errorLogin.push("Username should not be blank")" of code

Here is the code below:
I wanted to know the purpose of .push in this line: #errorLogin.push("Username should not be blank")
def loginValidations(errorLogin)
puts"inside loginValidations"
loginUsername = #params["username"]
puts "loginUsername: #{loginUsername}"
loginPass = #params["password"]
puts "loginPass: #{loginPass}"
logFlag=true
#count=0
if loginUsername == nil || loginUsername == ""
#errorLogin.push("Username should not be blank")
logFlag=false
end
#count=count+1
if loginPass == nil || loginPass == ""
#errorLogin.push("Password should not be blank")
logFlag=false
end
end
Array#push inserts an element at the end of the array.
a = [1,2,3]
a.push 4
# a = [1,2,3,4]
In that case probably that line inserts a failed validation in a variable that keeps track of login errors.

How can I find acronym in a text?

My project reads many files (these files have title text and sections) and should find the title of the files that contain an acronym. This is my docs class:
class Doc
def initialize(id, secciones)
#id, #secciones = id, secciones
end
def to_s
result = "" + #id.to_s + "\n" + #secciones.to_s
return result
end
def tiene_acronimo(acr)
puts "a ver si tiene acronimos el docu.."
tiene_acronimo = false
secciones.each do |seccion|
if seccion.tiene_acronimo(acr)
tiene_acronimo = true
end
end
return tiene_acronimo
end
attr_accessor :id
attr_accessor :secciones
end
And this my sections class:
class Section
def initialize ()
#title = ""
#text = ""
end
def tiene_acronimo(acr)
return title.include?(acr) || text.include?(acr)
end
end
And this my method:
def test()
results = Array.new
puts "Dame el acronimo"
acr = gets
documentos_cientificos.each do |d|
if d.tiene_acronimo(acr)
results << d
end
end
The method gets an acronym and should find all documents that contain it. The method inclue? [sic] ingores the upcase and returns true if the docs contain any substring like the acronym. For example:
Multiple sclerosis (**MS**), also known as # => `true`
Presenting signs and sympto**ms** # => `false` (but `include?` returns `true`)
How I can find an acronym more easily?
You could use some regex with the match function. The following regex will find a match if the content contains the FULL word provided. It will ignore substrings, and it will be case sensitive.
arc = "MS"
title = "Multiple sclerosis (MS), also known as"
text = "Presenting signs and symptoms"
title.match(/\b#{Regexp.escape(acr)}\b/) # => #<MatchData "MS">
text.match(/\b#{Regexp.escape(acr)}\b/) # => nil
or equivalently
title.match(/\b#{Regexp.escape(acr)}\b/).to_a.size > 0 # => true
text.match(/\b#{Regexp.escape(acr)}\b/).to_a.size > 0 # => false
...so you could redefine your function as:
def tiene_acronimo(acr)
regex_to_match = /\b#{Regexp.escape(acr)}\b/
has_acr = false
if (title.match(regex_to_match)) || (text.match(regex_to_match))
has_acr = true
end
return has_acr
end

Rewrite method using case in Ruby

So I have defined this class File inside a module and what I want to do is rewrite the self.parse method so that it uses case. I'm new to Ruby so this is not straightforward for me. Also the method must contain in it's body no more than 8 lines of code. Any ideas how to do it? Also I asked it on Code Review and they said it was off topic for them.
module RBFS
class File
attr_accessor :content
def initialize (data = nil)
#content = data
end
def data_type
case #content
when NilClass then :nil
when String then :string
when TrueClass , FalseClass then :boolean
when Float , Integer then :number
when Symbol then :symbol
end
end
def serialize
case #content
when NilClass then "nil:"
when String then "string:#{#content}"
when TrueClass , FalseClass then "boolean:#{#content}"
when Symbol then "symbol:#{#content}"
when Integer , Float then "number:#{#content}"
end
end
def self.parse (str)
arr = str.partition(':')
if arr[0] == "nil" then return File.new(nil) end
if arr[0] == "string" then return File.new(arr[2].to_s) end
if (arr[0] == "boolean" && arr[2].to_s == 'true') then return File.new(true) end
if (arr[0] == "boolean" && arr[2].to_s == 'false') then return File.new(false) end
if arr[0] == "symbol" then return File.new(arr[2].to_sym) end
return File.new(arr[2].to_i) if (arr[0] == "number" && arr[2].to_s.include?('.') == false)
return File.new(arr[2].to_f) if (arr[0] == "number" && arr[2].to_s.include?('.') == true)
end
end
end
Example how 'RBFS::File.parse' works:
RBFS::File.parse("string:"Hello world") => #<RBFS::File:0x1c45098 #content="Hello world"> #Tested in irb
I would personally prefer this:
def self.parse(arg)
key, value = arg.to_s.split(':')
{
'nil' => new(nil),
'string' => new(value),
'boolean' => new(value == 'true'),
'symbol' => new(value.to_sym),
'number' => new(value.include?('.') ? BigDecimal(value) : Integer(value))
}[key]
end
Code above is actually of 2 lines, broken into multiple lines for readability sake. However, if using case is a must then you can change your code to this:
def self.parse(arg)
key, value = arg.to_s.split(':')
case key
when 'nil' then new(nil)
when 'string' then new(value)
when 'boolean' then new(value == 'true')
when 'symbol' then new(value.to_sym)
when 'number' then new(value.include?('.') ? BigDecimal(value) : Integer(value))
end
end
In Ruby, case statements test using the case equality method #===. #=== returns true for several different of comparisons beyond the type checking you've already implemented in #serialize and #data_type. For example:
Integer === 1 //=> true
Numeric === 1 //=> true
(1..10) === 1 //=> true
1 === 1 //=> true
With that knowledge, we can construct a #parse method as follows:
def parse(serialized)
type, content = serialized.split(':') # A neat trick that'll make things more readable.
case type
when 'nil'
# ...
when 'string'
# ...
when 'boolean'
# ...
when 'symbol'
# ...
when 'number'
# ...
else
raise "Invalid RBFS file."
end
end
I'm not sure that you'll be able to do this in 8 lines without compromising the file's readability or dropping the error handling step I added at the end (which I highly recommend), but to get close, you can use the when ... then ... syntax.
Below is a suggestion for one way you might write this. It is untested, of course. I made two assumptions:
what you refer to as a[2] is a string, so a[2].to_s is unnecessary.
if a[0] => "boolean", a[2] is 'true' or 'false'.
module RBFS
class File
attr_accessor :content
def initialize (data = nil)
#content = data
end
def data_type
class_to_sym[#content.class]
end
def serialize
return nil if #content.class == NilClass
"#{class_to_sym[#content.class].to_s}:#{#content}"
end
def self.parse (str)
type,_,val = str.partition(':')
File.new(type_to_arg(type, val))
end
private
def class_to_sym
{ NilClass=>:nil, String=>:string, TrueClass=>:boolean,
FalseClass=>:boolean, Numeric=>:number, Symbol=>:symbol }
end
def type_to_arg(type, val)
case type
when "nil" then nil
when "string" then val
when "boolean" then val == 'true'
when "symbol" then val.to_sym
when "numeric" then val[/\./] ? val.to_f : val.to_i
end
end
end
end
end
If you prefer, you could replace val[/\./] with val.include?('.').
You could alternatively use a hash to simulate a case statement in type_to_arg:
def type_to_arg(type, val)
{ "nil" =>nil,
"string" =>val,
"boolean"=>(val=='true'),
"symbol" =>val.to_sym,
"number" =>val[/\./] ? val.to_f : val.to_i
}[type]
end

Resources