Can I iterate through an array during a comparison? - ruby-on-rails

s = Array.new
s << 19
while (s.last + 19) < 100000 do
s << s.last + 19
end
This^ works. s is an array of all factors of 19 below 100,000.
I'm trying to, in a succinct statement, find all numbers in s where the reverse of that number is also in the array. Ex: 176 and 671.
reflections= s.select { |num| num.to_s.reverse == s.each.to_s }
I know this is wrong, but how can I check each reversed item against the entire array?

This should work:
reflections = s.select { |num| s.include?(num.to_s.reverse.to_i) }
Although it produces results that you probably didn't anticipate
s = [176, 234, 671, 111]
reflections = s.select { |num| s.include?(num.to_s.reverse.to_i) }
reflections # => [176, 671, 111]
These are all valid results according to your logic.
Excluding self-match is pretty straighforward:
s = [176, 234, 671, 111]
reflections = s.select do |x|
x = x.to_s
r = x.reverse
(x != r) && s.include?(r.to_i)
end
reflections # => [176, 671]

reflections = s & s.map{|num| num.to_s.reverse.to_i}

Try:
reverse_array = s.select {|num| num.to_s == num.to_s.reverse }
UPDATE:
After checking I found this will work:
myarr = ""
s = (1..1000)
s.select{ |num|
unless s.include?(num.to_s.reverse.to_i)
myarr << num.to_s
end
}
Finally, the myarr will contain all the numbers whose reverse is present in array s.

Related

ArithGeo(arr) CoderByte Ruby: Why doesn't this solution work for certain test case scenarios

Below I have posted the instructions for this problem along with my solution. A few test case scenarios have failed, but seem to be working for most. Can anybody help out at which point I've gone wrong? Any help is much appreciated!!
Using the Ruby language, have the function ArithGeo(arr) take the array of numbers stored in arr and return the string "Arithmetic" if the sequence follows an arithmetic pattern or return "Geometric" if it follows a geometric pattern.
If the sequence doesn't follow either pattern return -1.
An arithmetic sequence is one where the difference between each of the numbers is consistent
Arithmetic example: [2, 4, 6, 8]
In a geometric sequence, each term after the first is multiplied by some constant or common ratio.
Geometric example: [2, 6, 18, 54]
Negative numbers may be entered as parameters, 0 will not be entered, and no array will contain all the same elements.
Code:
def arithGeo(num)
idx = 0
while idx < num.length
if ((num[idx] - num[idx + 1]) == (num[idx + 1] - num[idx + 2]))
return "Arithmetic"
elsif ((num[idx + 1] / num[idx]) == (num[idx + 2] / num[idx + 1]))
return "Geometric"
else
return "-1"
end
idx += 1
end
end
#Test Cases that Failed
p arithGeo([1, 2, 3, 4, 5, 10, 20])
p arithGeo([1, 2, 3, 4, 5, 6, 7, 88, 2])
p arithGeo([10, 110, 210, 310, 410, 511])
OK, lets do a much more "ruby like" way:
def arith?(arr)
check_arr = []
arr.reverse.inject {|memo, num| check_arr << (memo - num); num}
#loop through from highest to lowest, subtracting each from the next and store in check_arr
check_arr.all? {|num| num == check_arr[-1]}
#check that all results are the same in the arr i.e. [2,2,2,2,2]
end
This returns true if all of the operations return the same result, thus a linear progression.
def geo?(arr)
check_arr = []
arr.reverse.inject {|memo, num| check_arr << (memo / num); num}
#loop through from highest to lowest, dividing each by the next and store in check_arr
check_arr.all? {|x| x == check_arr[-1]}
#check that all products are the same in the arr i.e. [3,3,3,3,3]
end
This returns true if all of the operations return the same result, thus a geometric progression.
Now use those methods in your other method
def arith_geo?(arr)
if arith?(arr)
'Arithmetic'
elsif geo?(arr)
'Geometric'
else
-1
end
end
You did use a while but you do not loop over the data, because you write return you will only ever look at the first three numbers and then immediately return the result. You will have to keep the previous result, and make sure the result stays the same to return either geometric or arithmetic.
This should help you to complete the exercise :)
I was able to do the solution in JavaScript and this is what I came up with:
function algoGeo(arr){
var algo = true;
var geo = true;
//first check algo
for(var k = 1; k < arr.length; k++){
if( (arr[0] + (arr[1] - arr[0]) * k) !== arr[k] && algo ){
algo = false;
}
if( arr[0] * Math.pow(arr[1] / arr[0], k) !== arr[k] && geo){
geo = false;
}
}
return algo ? "Arithmetic" : geo ? "Geometric" : -1;
}
var arr = [5,12,19,26];
console.log(algoGeo(arr));
def ArithGeo(arr)
diff1 = []
diff2 = []
arr.each_index do |x|
if(x + 1 < arr.length)
diff1 << arr[x + 1] - arr[x]
diff2 << arr[x + 1] / arr[x]
end
end
diff1.uniq.size == 1 ? "Arithmetic" : diff2.uniq.size == 1 ? "Geometric" : -1
end
A little late but this is what i came up with when trying to solve this same question.

Using Ruby convert numbers to words?

How to convert numbers to words in ruby?
I know there is a gem somewhere. Trying to implement it without a gem. I just need the numbers to words in English for integers. Found this but it is very messy. If you have any idea on how to implement a cleaner easier to read solution please share.
http://raveendran.wordpress.com/2009/05/29/ruby-convert-number-to-english-word/
Here is what I have been working on. But having some problem implementing the scales. The code is still a mess. I hope to make it more readable when it functions properly.
class Numberswords
def in_words(n)
words_hash = {0=>"zero",1=>"one",2=>"two",3=>"three",4=>"four",5=>"five",6=>"six",7=>"seven",8=>"eight",9=>"nine",
10=>"ten",11=>"eleven",12=>"twelve",13=>"thirteen",14=>"fourteen",15=>"fifteen",16=>"sixteen",
17=>"seventeen", 18=>"eighteen",19=>"nineteen",
20=>"twenty",30=>"thirty",40=>"forty",50=>"fifty",60=>"sixty",70=>"seventy",80=>"eighty",90=>"ninety"}
scale = [000=>"",1000=>"thousand",1000000=>" million",1000000000=>" billion",1000000000000=>" trillion", 1000000000000000=>" quadrillion"]
if words_hash.has_key?(n)
words_hash[n]
#still working on this middle part. Anything above 999 will not work
elsif n>= 1000
print n.to_s.scan(/.{1,3}/) do |number|
print number
end
#print value = n.to_s.reverse.scan(/.{1,3}/).inject([]) { |first_part,second_part| first_part << (second_part == "000" ? "" : second_part.reverse.to_i.in_words) }
#(value.each_with_index.map { |first_part,second_part| first_part == "" ? "" : first_part + scale[second_part] }-[""]).reverse.join(" ")
elsif n <= 99
return [words_hash[n - n%10],words_hash[n%10]].join(" ")
else
words_hash.merge!({ 100=>"hundred" })
([(n%100 < 20 ? n%100 : n.to_s[2].to_i), n.to_s[1].to_i*10, 100, n.to_s[0].to_i]-[0]-[10])
.reverse.map { |num| words_hash[num] }.join(" ")
end
end
end
#test code
test = Numberswords.new
print test.in_words(200)
My take on this
def in_words(int)
numbers_to_name = {
1000000 => "million",
1000 => "thousand",
100 => "hundred",
90 => "ninety",
80 => "eighty",
70 => "seventy",
60 => "sixty",
50 => "fifty",
40 => "forty",
30 => "thirty",
20 => "twenty",
19=>"nineteen",
18=>"eighteen",
17=>"seventeen",
16=>"sixteen",
15=>"fifteen",
14=>"fourteen",
13=>"thirteen",
12=>"twelve",
11 => "eleven",
10 => "ten",
9 => "nine",
8 => "eight",
7 => "seven",
6 => "six",
5 => "five",
4 => "four",
3 => "three",
2 => "two",
1 => "one"
}
str = ""
numbers_to_name.each do |num, name|
if int == 0
return str
elsif int.to_s.length == 1 && int/num > 0
return str + "#{name}"
elsif int < 100 && int/num > 0
return str + "#{name}" if int%num == 0
return str + "#{name} " + in_words(int%num)
elsif int/num > 0
return str + in_words(int/num) + " #{name} " + in_words(int%num)
end
end
end
puts in_words(4) == "four"
puts in_words(27) == "twenty seven"
puts in_words(102) == "one hundred two"
puts in_words(38_079) == "thirty eight thousand seventy nine"
puts in_words(82102713) == "eighty two million one hundred two thousand seven hundred thirteen"
Have you considered humanize ?
https://github.com/radar/humanize
Simple answer use humanize gem and you will get desired output
Install it directly
gem install humanize
Or add it to your Gemfile
gem 'humanize'
And you can use it
require 'humanize'
1.humanize #=> 'one'
345.humanize #=> 'three hundred and forty-five'
1723323.humanize #=> 'one million, seven hundred and twenty-three thousand, three hundred and twenty-three'
If you are using this in rails you can directly use this
NOTE: As mentioned by sren in the comments below. The humanize method provided by ActiveSupport is different than the gem humanize
You can also use the to_words gem.
This Gem converts integers into words.
e.g.
1.to_words # one ,
100.to_words # one hundred ,
101.to_words # one hundred and one
It also converts negative numbers.
I can see what you're looking for, and you may wish to check out this StackOverflow post: Number to English Word Conversion Rails
Here it is in summary:
No, you have to write a function yourself. The closest thing to what
you want is number_to_human, but that does not convert 1 to One.
Here are some URLs that may be helpful:
http://codesnippets.joyent.com/posts/show/447
http://raveendran.wordpress.com/2009/05/29/ruby-convert-number-to-english-word/
http://deveiate.org/projects/Linguistics/
I am not quite sure, if this works for you. Method can be called like this.
n2w(33123) {|i| puts i unless i.to_s.empty?}
Here is the method ( I have not tested it fully. I think it works upto million. Code is ugly, there is a lot of room for re-factoring. )
def n2w(n)
words_hash = {0=>"zero",1=>"one",2=>"two",3=>"three",4=>"four",5=>"five",6=>"six",7=>"seven",8=>"eight",9=>"nine",
10=>"ten",11=>"eleven",12=>"twelve",13=>"thirteen",14=>"fourteen",15=>"fifteen",16=>"sixteen",
17=>"seventeen", 18=>"eighteen",19=>"nineteen",
20=>"twenty",30=>"thirty",40=>"forty",50=>"fifty",60=>"sixty",70=>"seventy",80=>"eighty",90=>"ninety"}
scale = {3=>"hundred",4 =>"thousand",6=>"million",9=>"billion"}
if words_hash.has_key?n
yield words_hash[n]
else
ns = n.to_s.split(//)
while ns.size > 0
if ns.size == 2
yield("and")
yield words_hash[(ns.join.to_i) - (ns.join.to_i)%10]
ns.shift
end
if ns.size > 4
yield(words_hash[(ns[0,2].join.to_i) - (ns[0,2].join.to_i) % 10])
else
yield(words_hash[ns[0].to_i])
end
yield(scale[ns.size])
ns.shift
end
end
end
def subhundred number
ones = %w{zero one two three four five six seven eight nine
ten eleven twelve thirteen fourteen fifteen
sixteen seventeen eighteen nineteen}
tens = %w{zero ten twenty thirty **forty** fifty sixty seventy eighty ninety}
subhundred = number % 100
return [ones[subhundred]] if subhundred < 20
return [tens[subhundred / 10]] if subhundred % 10 == 0
return [tens[subhundred / 10], ones[subhundred % 10]]
end
def subthousand number
hundreds = (number % 1000) / 100
tens = number % 100
s = []
s = subhundred(hundreds) + ["hundred"] unless hundreds == 0
s = s + ["and"] unless hundreds == 0 or tens == 0
s = s + [subhundred(tens)] unless tens == 0
end
def decimals number
return [] unless number.to_s['.']
digits = number.to_s.split('.')[1].split('').reverse
digits = digits.drop_while {|d| d.to_i == 0} . reverse
digits = digits.map {|d| subhundred d.to_i} . flatten
digits.empty? ? [] : ["and cents"] + digits
end
def words_from_numbers number
steps = [""] + %w{thousand million billion trillion quadrillion quintillion sextillion}
result = []
n = number.to_i
steps.each do |step|
x = n % 1000
unit = (step == "") ? [] : [step]
result = subthousand(x) + unit + result unless x == 0
n = n / 1000
end
result = ["zero"] if result.empty?
result = result + decimals(number)
result.join(' ').strip
end
def words_from_numbers(number)
ApplicationHelper.words_from_numbers(number)
end
Its been quite a while since the question was asked. Rails has something inbuilt for this now.
https://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html
number_to_human(1234567) # => "1.23 Million"
number_to_human(1234567890) # => "1.23 Billion"
number_to_human(1234567890123) # => "1.23 Trillion"
number_to_human(1234567890123456) # => "1.23 Quadrillion"
number_to_human(1234567890123456789) # => "1230 Quadrillion"

Calculate letter grade using a series of grades

Noob to Ruby here. Working through some exercises and have hit a wall.
Exercise: Calculate the letter grade of a series of grades
Create a method get_grade that accepts an Array of test scores. Each score in the array should be between 0 and 100, where 100 is the max score.
Compute the average score and return the letter grade as a String, i.e., 'A', 'B', 'C', 'D', 'E', or 'F'.
I keep returning the error:
avg.rb:1: syntax error, unexpected tLBRACK, expecting ')'
def get_grade([100,90,80])
^
avg.rb:1: syntax error, unexpected ')', expecting $end
Here's what I have so far. I'd like to stick with the methods below or .join as I'm trying to work with the methods we're learning in class. So sum, inject, etc won't necessarily be helpful. And I apologize in advance for the specificity of the request :) I'm sure there's a way better way that is way less code, but I'm just trying to learn it this way to start.
def get_grade([100,90,80])
get_grade = (array[0] + array[1] + array[2]).to_i / array.length.to_i
case get_grade
when 90..100
"A"
when 80..90
"B"
when 70..80
"C"
when 60..70
"D"
when 0..60
"F"
else
"Error"
end
end
puts get_grade([100,90,80])
You can't just randomly dump an array literal like [100,90,80] into the parameter list of a function definition. Judging by the function body, I think you meant to accept a single parameter array:
def get_grade(array)
grade = (array[0].to_i + array[1].to_i + array[2].to_i) / array.length
case grade
# unchanged
end
end
A terse replacement of the big case statement, for fun:
def letter_grade( score ) # assumes that score is between 0 and 100 (not 0-1)
%w[F F F F F F D C B A][ (score/10.0).floor ] || 'A' # handles grades >=100
end
Or, for more granularity:
def letter_grade( score ) # score is between 0 and 100 (not 0-1)
grades = %w[F F F F F F F F F F F F F F F F F F D- D D+ C- C C+ B- B B+ A- A A+ A+]
grades[ (3.0*score/10).floor ]
end
Thanks for the help today! Here's what I ended up doing to make it work with more than just 3 arguments. I used an Array#each method. I imagine there's a more elegant solution out there, but it worked! Worked on this since 10:00 AM, greatly appreciate the help!
def get_grade(array)
sum = 0
array.each do |element|
sum += element
end
average = sum / array.length
if average >= 90
grade = "A"
elsif average >= 80
grade = "B"
elsif average >= 70
grade = "C"
elsif average >= 60
grade = "D"
elsif average >= 0
grade = "F"
else
"Error"
end
end
puts get_grade([70,80,80,90,100])
puts get_grade([100,80,90,11,20])
puts get_grade([30,20,10,60,75])
Remember that the max score is 100 (and it can be assumed that the min is 0).
def get_grade(array)
sum = 0
array.each do |x|
sum += x
end
average = sum / array.length
if average > 100
print "Grades must be no more than 100!"
elsif average >= 90
grade = "A"
elsif average >= 80
grade = "B"
elsif average >= 70
grade = "C"
elsif average >= 60
grade = "D"
elsif average >=0
grade = "F"
else
print "Grades must be no less than 0!"
end
grade
end
puts get_grade([100,90,80]) == "A"
puts get_grade([98,90,80]) == "B"
puts get_grade([80,80,80]) == "B"
puts get_grade([55,45,35]) == "F"
puts get_grade([101,100,104])
puts get_grade([-2,-3,-4])
Added a proc so that even if a user enters a score over 100 it won't be calculated into the average.
Also refactored the switch statements to one line each. Let me know if this helps. Good luck.
def get_grade array
scores_under_100 = Proc.new {|score| score <= 100 && score > 0}
scores = array.select(&scores_under_100)
average = scores.inject(:+) / scores.size
case average
when 90..100 then puts "A."
when 80..89 then puts "B."
when 70..79 then puts "C."
when 60..69 then puts "D."
else puts "F."
end
end
puts get_grade([100, 100, 90, 67, 85, 200, 290, 299, 299])

Clean way to create a hash with 3 levels of nesting which creates levels if they don't exist

This is what I do now
h = Hash.new { |h1, k1| h1[k1] = Hash.new { |h2, k2| h2[k2] = {} } }
Although it works, it looks kind of ambiguous. Maybe there is a better way to do the same thing?
h = hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) }
Then you can assign in anyway you like,
h[:a][:b][:c][:d] = 3
Reference: ref
You can create one recursively like this.
def create n
return {} if n == 0
Hash.new {|h, k| h[k] = create(n - 1)}
end
h = create 3
h[1][1][1] = 2
p h[1][1][1] # => 2
p h[2][1][2] # => {}
h[2][1][2] = 3
p h # => {1=>{1=>{1=>2}}, 2=>{1=>{2=>3}}}
Your code is correct. You can just classify it:
class NestedHash < Hash
def initialize(depth)
self.default_proc = Proc.new {|h,k| h[k] = NestedHash.new(depth-1)} if depth && depth > 1
end
end
h = NestedHash.new(3)

Help refactoring this nasty Ruby if/else statement

So I have this big, hairy if/else statement. I pass a tracking number to it, and then it determines what type of tracking number it is.
How can I simplify this thing? Specifically wanting to reduce the number of lines of codes.
if num_length < 8
tracking_service = false
else
if number[1, 1] == 'Z'
tracking_service = 'ups'
elsif number[0, 1] == 'Q'
tracking_service = 'dhl'
elsif number[0, 2] == '96' && num_length == 22
tracking_service = 'fedex'
elsif number[0, 1] == 'H' && num_length == 11
tracking_service = 'ups'
elsif number[0, 1] == 'K' && num_length == 11
tracking_service = 'ups'
elsif num_length == 18 || num_length == 20
check_response(number)
else
case num_length
when 17
tracking_service = 'dhlgm'
when 13,20,22,30
tracking_service = 'usps'
when 12,15,19
tracking_service = 'fedex'
when 10,11
tracking_service = 'dhl'
else
tracking_service = false
end
end
end
Yes, I know. It's nasty.
Try this. I rewrote it using case and regular expressions. I also used :symbols instead of "strings" for the return values, but you can change that back.
tracking_service = case number
when /^.Z/ then :ups
when /^Q/ then :dhl
when /^96.{20}$/ then :fedex
when /^[HK].{10}$/ then :ups
else
check_response(number) if num_length == 18 || num_length == 20
case num_length
when 17 then :dhlgm
when 13, 20, 22, 30 then :usps
when 12, 15, 19 then :fedex
when 10, 11 then :dhl
else false
end
end
Depending on whether or not the tracking code is a ruby object, you could also put helper's in it's class definition:
class TrackingCode < String
# not sure if this makes sense for your use case
def ups?
self[1,1] == 'Z'
end
def dhl?
self[0,1] == 'Q'
end
def fedex?
self.length == 22 && self[0, 2] == '96'
end
# etc...
end
Then your conditional becomes:
if number.ups?
# ...
elsif number.dhl?
# ...
elseif number.fedex?
end
One simplified conditional where you are operating on the implied feature of the tracking code. Likewise, if you were to take a looping approach, your loop would also be cleaner:
%w(ups? dhl? fedex?).each do |is_code|
return if number.send(is_code)
end
or even:
%w(ups? dhl? fedex?).each do |is_code|
yield if number.send(is_code)
end
This method looks like it was written for speed. You can use a minhash as a substitute, but I think the code is fairly clean and doesn't require a refactor. Rubyists tend to be disgusted by needless structure, but oftentimes it's needed to model real-world situations and/or provides a performance boost. The keyword should be needless.
Whilst longer than jtbandes solution, you might like this as it's a bit more declarative:
class Condition
attr_reader :service_name, :predicate
def initialize(service_name, &block)
#service_name = service_name
#predicate = block
end
end
CONDITIONS = [
Condition.new('ups') { |n| n[1] == 'Z' },
Condition.new('dhl') { |n| n[0] == 'Q' },
Condition.new('fedex') { |n| n[0..1] == '96' && n.size == 22 },
Condition.new('ups') { |n| n[0] == 'H' && n.size == 11 },
Condition.new('ups') { |n| n[0] == 'K' && n.size == 11 },
Condition.new('dhlgm') { |n| n.size == 17 },
Condition.new('usps') { |n| [13, 20, 22, 30].include?(n.size) },
Condition.new('fedex') { |n| [12, 15, 19].include?(n.size) },
Condition.new('dhl') { |n| [10, 11].include?(n.size) },
]
def tracking_service(tracking_number)
result = CONDITIONS.find do |condition|
condition.predicate.call(tracking_number)
end
result.service_name if result
end
I haven't dealt with the check_response method call here as I feel you should probably handle that elsewhere (assuming it does something other than return a tracking service name).
I believe this is sufficiently complex to deserve its own method.
BTW, if the length is 20 then the original function returns whatever check_response(n) returns, yet then attempts (and will always fail) to return 'usps'.
#lenMap = Hash.new false
#lenMap[17] = 'dhlgm'
#lenMap[13] = #lenMap[20] = #lenMap[22] = #lenMap[30] = 'usps'
#lenMap[12] = #lenMap[15] = #lenMap[19] = 'fedex'
#lenMap[10] = #lenMap[11] = 'dhl'
def ts n
len = n.length
return false if len < 8
case n
when /^.Z/
return 'ups'
when /^Q/
return 'dhl'
when /^96....................$/
return 'fedex'
when /^[HK]..........$/
return 'ups'
end
return check_response n if len == 18 or len == 20
return #lenMap[len]
end
# test code...
def check_response n
return 'check 18/20 '
end
%w{ 1Zwhatever Qetcetcetc 9634567890123456789012 H2345678901
K2345678901 hownowhownowhownow hownowhownowhownow90
12345678901234567
1234567890123
12345678901234567890
1234567890123456789012
123456789012345678901234567890
123456789012
123456789012345
1234567890123456789
1234567890
12345678901 }.each do |s|
puts "%32s %s" % [s, (ts s).to_s]
end

Resources