Casting by a variable of class - ruby

I can cast a string to float via:
Float("1")
Now I have this:
f = Float
How can I use f to cast a string to float?
Edited
I'm sorry for not being clear.
Here is my scenario:
I'm using Redis DB, via gem redis-rb. Basically redis-db stores values as strings. When I map back values from DB to my objects, I wish to cast them to some primitive types for easy to use.
So I use this:
require 'ostruct'
class Person < OpenStruct
MAP = { :name => String, :age => Fixnum }
##
# This reads data from DB, and converts them to some "primitive" types
# for easy use.
#
def read
MAP.each_pair do |sym, cls|
# read data as string from DB, via key `sym.to_s`
s = ...
# now I have `cls`, how can I "cast" `s` to `cls`?
self[sym] = ???
# I know I can "iterate" all types by this:
#
# if cls.is_a? Float
# self[sym] = s.to_f
# elsif cls.is_a? Fixnum
# self[sym] = s.to_i
# ...
#
# But in Python I can just cast `s` to `cls` in one line...
# So I wonder if there is some way to cast `s` to `cls` in Ruby?
end
end # read
end # Person
Saying "for easy use", I mean I want to:
p = Person.new
p.read
# I want to access `age` as an integer, not a string
if p.age +-*/ ...

You cannot use f to do it. The Float in Float("1") is a method. The Float in f = Float is a class (an object). They are different things.

For what I understood from your question you want this:
result = '1'.to_f #outputs => 1.0
then for string:
result.to_s #outputs => "1.0"
And you can also do it in one step as well:
'1'.to_f.to_s #outputs => "1.0"
For more about strings and ruby you can see ruby doc

Related

How to convert to Crystal ruby's multiple assignments of Array

I have a small (formerly) ruby blockchain script I'm trying to convert over into Crystal, that looks like this so far:
# build your own blockchain from scratch in crystal!
#
# to run use:
# $ crystal ./blockchain_with_proof_of_work.cr
require "openssl" # for hash checksum digest function SHA256
class Block
getter index : Int32
getter timestamp : Time
getter data : String
getter previous_hash : String
getter nonce : Int32 # # proof of work if hash starts with leading zeros (00)
getter hash : String
def initialize(index, data, previous_hash)
#index = index
#timestamp = Time.now
#data = data
#previous_hash = previous_hash
#nonce, #hash = compute_hash_with_proof_of_work
end
def compute_hash_with_proof_of_work(difficulty = "00")
nonce = 0
loop do
hash = calc_hash_with_nonce(nonce)
if hash.starts_with?(difficulty)
return [nonce, hash] # # bingo! proof of work if hash starts with leading zeros (00)
else
nonce += 1 # # keep trying (and trying and trying)
end
end
end
def calc_hash_with_nonce(nonce = 0)
sha = OpenSSL::Digest.new("SHA256")
sha.update(nonce.to_s + #index.to_s + #timestamp.to_s + #data + #previous_hash)
sha.hexdigest
end
def self.first(data = "Genesis") # create genesis (big bang! first) block
# # uses index zero (0) and arbitrary previous_hash ("0")
Block.new(0, data, "0")
end
def self.next(previous, data = "Transaction Data...")
Block.new(previous.index + 1, data, previous.hash)
end
end # class Block
#####
# # let's get started
# # build a blockchain a block at a time
b0 = Block.first("Genesis")
b1 = Block.next(b0, "Transaction Data...")
b2 = Block.next(b1, "Transaction Data......")
b3 = Block.next(b2, "More Transaction Data...")
blockchain = [b0, b1, b2, b3]
puts blockchain
######
# will print something like:
#
# [#<Block:0x1e204f0
# #data="Genesis",
# #hash="00b8e77e27378f9aa0afbcea3a2882bb62f6663771dee053364beb1887e18bcf",
# #index=0,
# #nonce=242,
# #previous_hash="0",
# #timestamp=2017-09-20 20:13:38 +0200>,
# #<Block:0x1e56e20
# #data="Transaction Data...",
# #hash="00aae8d2e9387e13c71b33f8cd205d336ac250d2828011f5970062912985a9af",
# #index=1,
# #nonce=46,
# #previous_hash=
# "00b8e77e27378f9aa0afbcea3a2882bb62f6663771dee053364beb1887e18bcf",
# #timestamp=2017-09-20 20:13:38 +0200>,
# #<Block:0x1e2bd58
# #data="Transaction Data......",
# #hash="00ea45e0f4683c3bec4364f349ee2b6816be0c9fd95cfd5ffcc6ed572c62f190",
# #index=2,
# #nonce=350,
# #previous_hash=
# "00aae8d2e9387e13c71b33f8cd205d336ac250d2828011f5970062912985a9af",
# #timestamp=2017-09-20 20:13:38 +0200>,
# #<Block:0x1fa8338
# #data="More Transaction Data...",
# #hash="00436f0fca677652963e904ce4c624606a255946b921132d5b1f70f7d86c4ab8",
# #index=3,
# #nonce=59,
# #previous_hash=
# "00ea45e0f4683c3bec4364f349ee2b6816be0c9fd95cfd5ffcc6ed572c62f190",
# #timestamp=2017-09-20 20:13:38 +0200>]
However when I run it I get an error that states:
Error in blockchain.cr/blockchain_with_proof_of_work.cr:57: instantiating
'Block:Class#first(String)'
b0 = Block.first("Genesis")
^~~~~
in blockchain.cr/blockchain_with_proof_of_work.cr:45: instantiating
'Block:Class#new(Int32, String, String)'
Block.new(0, data, "0")
^~~
in blockchain.cr/blockchain_with_proof_of_work.cr:22: instance variable
'#nonce' of Block must be Int32, not (Int32 | String)
#nonce, #hash = compute_hash_with_proof_of_work
^~~~~~
Looking at Crystal docs on multiple assignment, I'm unsure of how I can refactor this method so that it doesn't fail Crystal's automatic static type checking and type inference? The method in question, of an array of two types being returned, doesn't seem covered by the docs:
#nonce, #hash = compute_hash_with_proof_of_work # return [nonce, hash]
When decomposing an Array into a multiple assignment Crystal can't infer the exact type of each element. So the value assigned to the instance variable #nonce could be either Int32 or String. You should use a Tuple instead: return {nonce, hash} (in line 29). A tuple has positional type declarations and is by the way more performant than an Array because it does not allocate memory on the heap.

Accidental Type conversion in Ruby with strings

I'm trying to solve a challenge where you take in a string of words but return the longest word in the string. My strategy is to break the string into an array of individual words and search the array. However, I'm getting a type conversion error. What is causing the type conversion error? This is particularly strange to me because I don't actually see any type conversion happening here.
def LongestWord(sen)
sen1 = sen.split("/\W+/")
grt = 0
sen1.each do |i|
if sen1[i].length > sen1[grt].length # Type conversion error
grt = i
end
end
sen1[grt]
end
# keep this function call here
puts LongestWord(STDIN.gets)
The type conversion is caused by the array entry i being converted (probably unsuccessfully) into an integer (though I suppose it could be ruby trying to convert the array into a hash, and use i as a key to the hash).
Your misunderstanding is that you think you're getting the array's indices passed into the block for each. What is passed in to that block is each individual value in the array. I.e., if your string sen is 'this is a silly string', then the values passed are 'this', 'is', 'a', 'silly', and 'string'.
You get the error because, when the code is running, i is the first value of sen1, which results in sen1['some string'] being evaluated.
An array can't have a string index, only a hash can, resulting in the Type error.
Meditate on this:
def longest_word(sen)
sen1 = sen.split # => ["foo", "barbaz"]
grt = 0
sen1.each do |i|
i # => "foo"
sen1 # => ["foo", "barbaz"]
sen1[i] # =>
sen1[grt] # =>
sen1[i].length # =>
sen1[grt].length # =>
if sen1[i].length > sen1[grt].length #Type conversion error
grt = i # =>
end
end
sen1[grt]
end
# keep this function call here
longest_word('foo barbaz')
Breaking it down further, here's the offending problem:
sen1 = 'foo barbaz'.split
sen1['foo'] # =>
# ~> TypeError
# ~> no implicit conversion of String into Integer
You don't see the type conversion, but it is there. In more than one place.
As Derrell Durrett pointed out in his answer, your are assuming (wrongly) that the index of the array is passed to the block, not its elements.
Then you write if sen1[i].length > sen1[grt].length. Let's consider the string is 'this is a silly string'. The first element is 'this' and what you are trying to do is if sen1['this'].length > sen1[0].length. As Ruby arrays always have integer indexes, Ruby tries to convert 'this' to an integer in order to find the element at the specified position. Of course this fails!
But your code is not that far from being right. A few small changes and it will run perfectly well:
def longest_word(sen)
sen1 = sen.split(" ")
grt = 0
sen1.each_index do |i|
if sen1[i].length > sen1[grt].length
grt = i
end
end
sen1[grt]
end
puts LongestWord(STDIN.gets)
Now you'd be passing the indexes with sen1.each_index and it'd be working fine.
Notice that I changed the name of your method to longest_word. This is much better, in fact, because this first capital letter is reserved to constants and class names.
I also would like to point that you are not using a good Ruby style. This could be written like this:
def longest_word(str)
str.split(" ").max_by{ |s| s.length }
end
and the result would be the same.

using an alias name for hash key value

I have some json data that I receive and that I JSON.parse to a hash. The hash key names are integer strings like data["0"], data["1"], data["2"], etc... where each value correspond to a state. Like 0 => START, 1 => STOP, 2 => RESTART.
I can't change the source json data to make the key more readable. Each hash will have 5 pairs that correspond to 5 different states.
I was wondering if there was a nice way for me to alias the numbers as meaningful names so when referencing the hash key value I don't have to use the number.
At the moment I'm using constants like below, but was thinking there might be a nicer, more Ruby way. Use another hash or struct so I can use data[STATES.start] or something?
STATE_START = "0"
STATE_STOP = "1"
STATE_RESTART = "2"
data = JSON.parse value
puts data[STATE_START]
Thanks
I think constants are fine. But if you want to rubify this code a bit, you can, for example, wrap the source hash in an object that will translate method names.
class MyHash
def initialize(hash)
#hash = hash
end
MAPPING = {
start: '0',
stop: '1',
restart: '2',
}
# dynamically define methods like
#
# def start
# #hash['0']
# end
#
# or you can use method_missing
MAPPING.each do |method_name, hash_key|
define_method method_name do
#hash[hash_key]
end
end
end
mh = MyHash.new({'0' => 'foo', '1' => 'bar'})
mh.start # => "foo"
mh.stop # => "bar"

Ruby: How to convert IP range to array of IP's

Is there any easy way to convert IP range to array of IPs?
def convertIPrange (start_ip, end_ip)
#output: array of ips end
end
e.g. input
('192.168.1.105', '192.168.1.108')
output
['192.168.1.105','192.158.1.106','192.158.1.107','192.158.1.108']
Use the Ruby standard library IPAddr
# I would suggest naming your function using underscore rather than camelcase
# because of Ruby naming conventions
#
require 'ipaddr'
def convert_ip_range(start_ip, end_ip)
start_ip = IPAddr.new(start_ip)
end_ip = IPAddr.new(end_ip)
# map to_s if you like, you can also call to_a,
# IPAddrs have some neat functions regarding IPs,
# be sure to check them out
#
(start_ip..end_ip).map(&:to_s)
end
def convertIPrange first, last
first, last = [first, last]
.map{|s| s.split(".").inject(0){|i, s| i = 256 * i + s.to_i}}
(first..last).map do |q|
a = []
(q, r = q.divmod(256)) && a.unshift(r) until q.zero?
a.join(".")
end
end
convertIPrange('192.168.1.105', '192.168.1.108')
# => ["192.168.1.105", "192.168.1.106", "192.168.1.107", "192.168.1.108"]
convertIPrange('192.255.255.254', '193.0.0.1')
# => ["192.255.255.254", "192.255.255.255", "193.0.0.0", "193.0.0.1"]

Special syntax for declaring Ruby objects

Everyone knows two of the ways to create an empty array: Array.new and []. The first one is 'standard', you might say, and the second one is simply syntax sugar. Many different objects such as Hash and maybe even String are shorthanded through this method.
My question is: Is there a way to define my own delimimers for objects? An example would be <>. Maybe an alias like '<' => 'MyObject.new(' and '>' => ')'?
[] is an array literal, {} is a hash literal. There are plenty of these shorthand forms in Ruby. Check this wikibook out for more information.
There is no object literal, but you can use (source):
a = Struct.new(:foo,:bar).new(34,89)
a.foo # 34
a.bar # 89
No. (And ew anyway.) Delimiters are part of the parse process.
You can define operators, like <; that's different than a delimiter. For example, you could redefine < to take a block, and use that block to create a class, or a method, etc. But... I don't think I would.
You could do:
class MyObject; end
def [](*args)
MyObject.new *args
end
# but you can't use it directly:
o = [] #=> [] (empty Array)
# you must to refer to self:
o = self[] #=> #<MyObject:0x1234567>
# but since self depends on where are you, you must assign self to a global variable:
$s = self
o = $s[]
# or to a constant:
S = self
o = S[]
# however, in that case it's better to do it in the proper class:
class << MyObject
def [](*args)
new *args
end
end
# and assign it to a single-letter constant to reduce characters:
S = MyObject
# so
o = S[] #=> #<MyObject:0x1234568>
I can't think on something more compact.

Resources