Convert integer to binary string, padded with leading zeros - ruby-on-rails

I want to create new method Integer#to_bin that convert decimal to a binary string. The argument of #to_bin is the number of digits. The result should be padded with leading zeros to make it have that many digits.
Example:
1.to_bin(4)
#=> "0001"
1.to_bin(3)
#=> "001"
1.to_bin(2)
#=> "01"
7.to_bin(1)
#=> nil
7.to_bin
#=> "111"
etс.
What I've tried:
class Integer
def to_bin(number=nil)
if number == nil
return self.to_s(2)
else
s = self.to_s(2).size
e = number-s
one = '0'
two = '00'
three = '000'
if e==one.size
one+self.to_s(2)
elsif e==two.size
two+self.to_s(2)
elsif e==three.size
three+self.to_s(2)
end
end
end
end
How do I convert an integer to a binary string padded with leading zeros?

The appropriate way to do this is to use Kernel's sprintf formatting:
'%03b' % 1 # => "001"
'%03b' % 2 # => "010"
'%03b' % 7 # => "111"
'%08b' % 1 # => "00000001"
'%08b' % 2 # => "00000010"
'%08b' % 7 # => "00000111"
But wait, there's more!:
'%0*b' % [3, 1] # => "001"
'%0*b' % [3, 2] # => "010"
'%0*b' % [3, 7] # => "111"
'%0*b' % [8, 1] # => "00000001"
'%0*b' % [8, 2] # => "00000010"
'%0*b' % [8, 7] # => "00000111"
So defining a method to extend Fixnum or Integer is easy and cleanly done:
class Integer
def to_bin(width)
'%0*b' % [width, self]
end
end
1.to_bin(8) # => "00000001"
0x55.to_bin(8) # => "01010101"
0xaaa.to_bin(16) # => "0000101010101010"

Ruby already has a built-in mechanism to convert a number to binary: #to_s accepts a base to convert to.
30.to_s(2) # => "11110"
If you want to left-pad it with zeroes:
30.to_s(2).rjust(10, "0") => "0000011110"
You could extend this into a little method that combines the two:
class Fixnum
def to_bin(width = 1)
to_s(2).rjust(width, "0")
end
end
> 1234.to_bin
=> "10011010010"
> 1234.to_bin(20)
=> "00000000010011010010"

Related

How to convert human readable number to actual number in Ruby?

Is there a simple Rails/Ruby helper function to help you convert human readable numbers to actual numbers?
Such as:
1K => 1000
2M => 2,000,000
2.2K => 2200
1,500 => 1500
50 => 50
5.5M => 5500000
test = {
'1K' => 1000,
'2M' => 2000000,
'2.2K' => 2200,
'1,500' => 1500,
'50' => 50,
'5.5M' => 5500000
}
class String
def human_readable_to_i
multiplier = {'K' => 1_000, 'M' => 1_000_000}[self.upcase[/[KM](?=\z)/]] || 1
value = self.gsub(/[^\d.]/, '')
case value.count('.')
when 0 then value.to_i
when 1 then value.to_f
else 0
end * multiplier
end
end
test.each { |k, v| raise "Test failed" unless k.human_readable_to_i == v }
Try something like this if you have an array of human readable numbers than
array.map do |elem|
elem = elem.gsub('$','')
if elem.include? 'B'
elem.to_f * 1000000000
elsif elem.include? 'M'
elem.to_f * 1000000
elsif elem.include? 'K'
elem.to_f * 1000
else
elem.to_f
end
end
Have a look here as well, you will find many Numbers Helpers
NumberHelper Rails.
Ruby Array human readable to actual

What's a good way to create a string array in Ruby based on integer variables?

The integer variables are:
toonie = 2, loonie = 1, quarter = 1, dime = 0, nickel = 1, penny = 3
I want the final output to be
"2 toonies, 1 loonie, 1 quarter, 1 nickel, 3 pennies"
Is there a way to interpolate this all from Ruby code inside [] array brackets and then add .join(", ")?
Or will I have to declare an empty array first, and then write some Ruby code to add to the array if the integer variable is greater than 0?
I would do something like this:
coins = { toonie: 2, loonie: 1, quarter: 1, dime: 0, nickel: 1, penny: 3 }
coins.map { |k, v| pluralize(v, k) if v > 0 }.compact.join(', ')
#=> "2 toonie, 1 loonie, 1 quarter, 1 nickel, 3 penny"
Note that pluralize is a ActionView::Helpers::TextHelper method. Therefore it is only available in views and helpers.
When you want to use your example outside of views, you might want to use pluralize from ActiveSupport instead - what makes the solution slightly longer:
coins.map { |k, v| "#{v} #{v == 1 ? k : k.pluralize}" if v > 0 }.compact.join(', ')
#=> "2 toonie, 1 loonie, 1 quarter, 1 nickel, 3 penny"
Can be done in rails:
hash = {
"toonie" => 2,
"loonie" => 1,
"quarter" => 1,
"dime" => 0,
"nickel" => 1,
"penny" => 3
}
hash.to_a.map { |ele| "#{ele.last} #{ele.last> 1 ? ele.first.pluralize : ele.first}" }.join(", ")
Basically what you do is convert the hash to an array, which will look like this:
[["toonie", 2], ["loonie", 1], ["quarter", 1], ["dime", 0], ["nickel", 1], ["penny", 3]]
Then you map each element to the function provided, which takes the inner array, takes the numeric value in the last entry, places it in a string and then adds the plural or singular value based on the numeric value you just checked. And finally merge it all together
=> "2 toonies, 1 loonie, 1 quarter, 1 nickel, 3 pennies"
I'm not sure what exactly you're looking for, but I would start with a hash like:
coins = {"toonie" => 2, "loonie" => 1, "quarter" => 1, "dime" => 0, "nickel" => 1, "penny" => 3}
then you can use this to print the counts
def coin_counts(coins)
(coins.keys.select { |coin| coins[coin] > 0}.map {|coin| coins[coin].to_s + " " + coin}).join(", ")
end
If you would like appropriate pluralizing, you can do the following:
include ActionView::Helpers::TextHelper
def coin_counts(coins)
(coins.keys.select { |coin| coins[coin] > 0}.map {|coin| pluralize(coins[coin], coin)}).join(", ")
end
This is just for fun and should not be used in production but you can achieve it like
def run
toonie = 2
loonie = 1
quarter = 1
dime = 0
nickel = 1
penny = 3
Kernel::local_variables.each_with_object([]) { |var, array|
next if eval(var.to_s).to_i.zero?
array << "#{eval(var.to_s)} #{var}"
}.join(', ')
end
run # returns "2 toonie, 1 loonie, 1 quarter, 1 nickel, 3 penny"
The above does not implement the pluralization requirement because it really depends if you will have irregular plural nouns or whatever.
I would go with a hash solution as described in the other answers

Converting hash ruby objects to positive currency

I have a hash where the keys are the months and I want to convert the objects to positive numbers AND currency.
INPUT
hash = {
12 => -5888.969999999999,
4 => -6346.1,
3 => -6081.76,
2 => -5774.799999999999,
1 => -4454.38
}
OUTPUT
hash = {
12 => 5888.96,
4 => 6346.10,
3 => 6081.76,
2 => 5774.79,
1 => 4454.38
}
#Output should be a float
Any help would be greatly appreciated.
Try
hash.transform_values{|v| v.round(2).abs()}
or
hash.update(hash){|k,v| v.round(2).abs()}
Numeric.abs() can be applied to ensure a number is positive and Float.round(2) will round a float to 2 decimal places. See ruby-doc.org/core-2.1.4/Numeric.html#method-i-abs and ruby-doc.org/core-2.2.2/Float.html#method-i-round for usage examples. Note that round() will not add trailing zeros since that does not affect numerical value, however trailing zeros can be added by formatting, for example:
hash = {
12 => -5888.969999999999,
4 => -6346.1,
3 => -6081.76,
2 => -5774.799999999999,
1 => -4454.38
}
# transform hash values
hash.each do |key, value|
hash[key] = value.abs().round(2)
end
# print the modified hash without formatting the values
hash.each do |key, value|
puts "#{key} => #{value}"
end
# prints
# 12 => 5888.97
# 4 => 6346.1
# 3 => 6081.76
# 2 => 5774.80
# 1 => 4454.38
# print hash with values formatted with precision of 2 digits
hash.each do |key, value|
puts "#{key} => #{'%.2f' % value}"
end
# prints
# 12 => 5888.97
# 4 => 6346.10
# 3 => 6081.76
# 2 => 5774.80
# 1 => 4454.38

Hash and frequency of a value

I have the following hash in Ruby :
{
0 => {
:method=> "POST",
:path=> "/api/customer/191023",
:host=> "host.8",
:duration=> "1221"
},
1 => {
:method=> "GET",
:path=> "/api/customer/191023",
:host=> "host.8",
:duration=> "99"
},
2 => {
:method=> "POST",
:path=> "/api/customer/191023",
:host=> "host.10",
:duration=> "142"
},
3 => {
:method=> "POST",
:path=> "/api/customer/191023",
:host=> "host.8",
:duration=> "243"
}
4 => {
:method=> "POST",
:path=> "/api/customer/191023",
:host=> "host.10",
:duration=> "132"
}
}
I would like to do a simple search within these hashes to find the host with the highest frequency. For example, in the previous example, I should get host.8.
Thank you for your help,
M.
To find host value with highest frequency do:
hs = hash.values.group_by { |h| h[:host] =~ /host\.(\d+)/ && $1.to_i || 0 }.to_a
hs.reduce([-1,0]) { |sum,v| v[1].size > sum[1] && [ v[0], v[1].size ] || sum }.first
Description: [-1,0] is the default value for set for #reduce method, where -1 is a number (like in host.number), and 0 is a count of the number. So, when reduce encounters the number with size more than of passed sum, it replaces with the new value on next iteration.
Here's one way to do that.
Code
def max_host(hash)
hash.each_with_object(Hash.new(0)) { |(_,v),h| h[v[:host]] += 1 }
.max_by { |_,v| v }
.first
end
Example
Let's take the simplified example below. Note that I've changed, for example, :host = \"host.10\" to :host = "host.10", as the former is not a correct syntax. You could write the string as '\"host.10\" (=> "\\\"host.10\\\""), but I assume you simply want "host.10". The code is the same for both.
hash = {
0 => {
:method=>"POST",
:host =>"host.8"
},
1 => {
:method=>"GET",
:host =>"host.10"
},
2 => {
:method=>"POST",
:host =>"host.10"
}
}
max_host(hash)
#=> "host.10"
Explanation
For the example hash above,
enum = hash.each_with_object(Hash.new(0))
#=> #<Enumerator: {
# 0=>{:method=>"POST", :host=>"host.8"},
# 1=>{:method=>"GET", :host=>"host.10"},
# 2=>{:method=>"POST", :host=>"host.10"}}:each_with_object({})>
The enumerator will invoke the method Hash#each to pass each element of the enumerator into the block. We can see what those elements are by converting the enumerator to an array:
enum.to_a
#=> [[[0, {:method=>"POST", :host=>"host.8"}], {}],
# [[1, {:method=>"GET", :host=>"host.10"}], {}],
# [[2, {:method=>"POST", :host=>"host.10"}], {}]]
The empty hash shown in the first element is the initial value of the hash created by
Hash.new(0)
This creates a hash h with a default value of zero. By doing it this way, if h does not have a key k, h[k] will return the default value (0), but (important!) this does not change the hash.
The first value passed into the block is
[[0, {:method=>"POST", :host=>"host.8"}], {}]
This is then decomposed (or "disambiguated") into individual objects that are assigned to three block variables:
k => 0
v => {:method=>"POST", :host=>"host.8"}
h => Hash.new(0)
We then execute:
h[v[:host]] += 1
which is
h["host.8"] += 1
which is shorthand for
h["host.8"] = h["host.8"] + 1
[Aside: you may have noticed that in the code I show the block variables as |(_,v),h|, whereas above I refer to them above as |(k,v),h|. I could have used the latter, but since k is not reference in the block, I've chosen to replace it with a "placeholder" _. This ensures k won't be referenced and also tells any readers that I'm not using what would be the first block variable.]
As h does not have a key "host.8", h["host.8"] to the right of = returns the default value:
h["host.8"] = 0 + 1
#=> 1
so now
h #=> {"host.8"=>1}
The second element passed into the block is
[[1, {:method=>"GET", :host=>"host.10"}], {"host.8"=>1}]
so the block variables become:
v => {:method=>"GET", :host=>"host.10"}
h => {"host.8"=>1}
Notice that the hash h has been updated. We execute
h[v[:host]] += 1
#=> h["host.10"] += 1
#=> h["host.10"] = h["host.10"] + 1
#=> h["host.10"] = 0 + 1
#=> 1
so now
h #=> {"host.8"=>1, "host.10"=>1}
Lastly, the block variables are assigned the values
v = {:method=>"POST", :host=>"host.10"}
h => {"host.8"=>1, "host.10"=>1}
so
h[v[:host]] += 1
#=> h["host.10"] += 1
#=> h["host.10"] = h["host.10"] + 1
#=> h["host.10"] = 1 + 1
#=> 2
h #=> {"host.8"=>1, "host.10"=>2}
and the value of h is returned by the method.

Creating a range from one column

I have a column called "Marks" which contains values like
Marks = [100,200,150,157,....]
I need to assign Grades to those marks using the following key
<25=0, <75=1, <125=2, <250=3, <500=4, >500=5
If Marks < 25, then Grade = 0, if marks < 75 then grade = 1.
I can sort the results and find the first record that matches using Ruby's find function. Is it the best method ? Or is there a way by which I can prepare a range using the key by adding Lower Limit and Upper Limit columns to the table and by populating those ranges using the key? Marks can have decimals too Ex: 99.99
Without using Rails, you could do it like this:
marks = [100, 200, 150, 157, 692, 12]
marks_to_grade = { 25=>0, 75=>1, 125=>2, 250=>3, 500=>4, Float::INFINITY=>5 }
Hash[marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }]
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
With Ruby 2.1, you could write this:
marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }.to_h
Here's what's happening:
Enumerable#map (a.k.a collect) converts each mark m to an array [m, g], where g is the grade computed for that mark. For example, when map passes the first element of marks into its block, we have:
m = 100
a = marks_to_grade.find { |k,_| m <= k }
#=> marks_to_grade.find { |k,_| 100 <= k }
#=> [125, 2]
a.last
#=> 2
so the mark 100 is mapped to [100, 2]. (I've replaced the block variable for the value of the key-value pair with the placeholder _ to draw attention to the fact that the value is not being used in the calculation within the block. One could also use, say, _v as the placeholder.) The remaining marks are similarly mapped, resulting in:
b = marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }
#=> [[100, 2], [200, 3], [150, 3], [157, 3], [692, 5], [12, 0]]
Lastly
Hash[b]
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
or, for Ruby 2.1+
b.to_h
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
You can make use of update_all:
Student.where(:mark => 0...25).update_all(grade: 0)
Student.where(:mark => 25...75).update_all(grade: 1)
Student.where(:mark => 75...125).update_all(grade: 2)
Student.where(:mark => 125...250).update_all(grade: 3)
Student.where(:mark => 250...500).update_all(grade: 4)
Student.where("mark > ?", 500).update_all(grade: 5)

Resources