2

I need the shipping cost to be determined by the various rates.

I've tried a hodgepodge of things for the last 5 hours. If I'm supposed to use .to_s .to_f, I've tried and done so incorrectly.

if (weight < 2)

     rate = 0.10

   elsif ((weight >= 2) or (weight < 10))

     rate = 0.20

   elsif ((weight >= 10) or (weight < 40))

     rate = 0.30

   elsif ((weight >= 40) or (weight < 70))

     rate = 0.50

   elsif ((weight >= 70) or (weight < 100))

     rate = 0.75

   else (weight >= 100)

     rate = 0.90

end

rate = rate.to_i

ship_cost = weight * price * rate

ship_cost = ship_cost.to_i

The result is supposed to show a shipping cost after the rate is applied. I keep getting to the String to Integer error.

10
  • What's the whole error you're getting? Commented Nov 2, 2019 at 13:33
  • And also please provide samples data and specific expected results Commented Nov 2, 2019 at 13:36
  • The whole error: :in `*': no implicit conversion of String into Integer (TypeError) Commented Nov 2, 2019 at 13:39
  • []['foo'] Traceback (most recent call last): 6: from /Users/user/.rbenv/versions/2.6.3/bin/irb:23:in '<main>' 5: from /Users/user/.rbenv/versions/2.6.3/bin/irb:23:in 'load' 4: from /Users/user/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in '<top (required)>' 3: from (irb):6 2: from (irb):6:in 'rescue in irb_binding' 1: from (irb):6:in '[]' TypeError (no implicit conversion of String into Integer), that's a whole error, what you provided is not. It tells nothing to people reading your question. Commented Nov 2, 2019 at 13:44
  • /Users/gemarh/RubymineProjects/Ch4Asg.rb/Ch4Asg.rb:35:in *': no implicit conversion of String into Integer (TypeError) from /Users/gemarh/RubymineProjects/Ch4Asg.rb/Ch4Asg.rb:35:in <main>' Commented Nov 2, 2019 at 13:48

2 Answers 2

2

The problem is somehow one or more variables within the multiplication is a string, which results in the TypeError error you're getting, as in:

'a' * 'b' #  '*': no implicit conversion of String into Integer (TypeError)

If you want to suppress the error, you can manually convert them into integers or floats. Meaning if the string doesn't have a numeric representation, it'll return 0:

'asd'.to_i  # 0
'1'.to_i.   # 1
'-9.9'.to_i # -9
'-9.9'.to_f # -9.9

Alternatively, you can handle the rate assignation by using a "dictionary" which holds the min and max value weight can be to return X. Creating a range from min to max and asking if it includes the value of weight you can get assign its value:

dict = {
  [-Float::INFINITY, 2]  => 0.10,
  [2, 10]                => 0.20,
  [10, 40]               => 0.30,
  [40, 70]               => 0.50,
  [70, 100]              => 0.75,
  [100, Float::INFINITY] => 0.90
}

p dict.find { |(start, finish), _| (start...finish).include?(-42.12) }.last # 0.1
p dict.find { |(start, finish), _| (start...finish).include?(0) }.last      # 0.1
p dict.find { |(start, finish), _| (start...finish).include?(1) }.last      # 0.1
p dict.find { |(start, finish), _| (start...finish).include?(23) }.last     # 0.3
p dict.find { |(start, finish), _| (start...finish).include?(101) }.last    # 0.9
Sign up to request clarification or add additional context in comments.

2 Comments

Holy crap I figured it out and I can't believe it was so easy. Thank you for helping me!!!!!!
If you are using an excusive range you need to define it as 40...70 not 41...70. Otherwise you will get the wrong values for 40.1.
1

A less verbose and more idiomatically correct solution is to use a case statement with ranges:

def shipping_rate(weight)
  case weight
  when 0...2
     0.10
  when 2...10
     0.20
  when 10...40
     0.30
  when 40...70
     0.50
  when 70...100
     0.75
  when 100...Float::INFINITY
     0.90
  end
end

Declaring a range with ... excludes the end value. So (40...70).cover?(70) == false. That lets us avoid overlap issues.

require "minitest/autorun"
class TestShippingRate < Minitest::Test
  def test_correct_rate
    assert_equal 0.10, shipping_rate(1)
    assert_equal 0.20, shipping_rate(3)
    assert_equal 0.30, shipping_rate(39)
    assert_equal 0.50, shipping_rate(40)
    assert_equal 0.75, shipping_rate(70)
    assert_equal 0.90, shipping_rate(101)
  end
end

# Finished in 0.002255s, 443.3896 runs/s, 2660.3374 assertions/s.
# 1 runs, 6 assertions, 0 failures, 0 errors, 0 skips

If you want to use a dict like Sebastian Palma suggested you can do it with hash with ranges for keys instead:

def shipping_rate(weight)
  {
    0...2 => 0.10,
    2...10 => 0.20,
    10...40 => 0.30,
    40...70 => 0.50,
    70...100 => 0.75,
    100...Float::INFINITY => 0.90
  }.find { |k, v| break v if k.cover? weight }
end

Using case is a bit more flexible though as you can add a else condition or handle string arguments:

def shipping_rate(weight)
  case weight
  when 0...2
     0.10
  when 2...10
     0.20
  when 10...40
     0.30
  when 40...70
     0.50
  when 70...100
     0.75
  when 100...Float::INFINITY
     0.90
  # I'm not saying this is a good idea as the conversion should happen
  # upstream. Its just an example of what you can do
  when String
     shipping_rate(weight.to_f) # recursion
  else 
     raise "Oh noes. This should not happen."
  end
end

1 Comment

I didn't think about the ranges as keys. Pretty nice!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.