Search code examples
rubysumsplat

ruby: adding numbers and printing true when sums are 21


It's a simple problem given on rubeque.com: write a method that takes any number of integers and adds them to return true if the sum is 21. False otherwise. It tests the input with:

assert_equal twenty_one?(3, 4, 5, 6, 3), true
assert_equal twenty_one?(3, 11, 10), false

Here's what I have so far:

def twenty_one?(*nums)
  nums.inject(&:+)
end

if twenty_one? == 21 
  puts true
else 
  false
end    

But I get the error message:

RuntimeError: The value '21' does not equal 'true'.

I'm really confused on how to fix this. Is it possible to put the if/else statement within the method? And sorry if this question is really basic. I'm new to programming.


Solution

  • You need to write your method as

    def twenty_one?(*nums)
      nums.inject(&:+) == 21
    end
    

    Here is one little demo :-

    require "minitest/autorun"
    
    class Numerics
      def twenty_one?(*nums)
        nums.inject(&:+) == 21
      end
    end
    
    class TestNumerics < MiniTest::Test
      def setup
        @numeric = Numerics.new
      end
    
      def teardown
        @numeric = nil
      end
    
      def test_twenty_one?
        assert_equal @numeric.twenty_one?(3, 4, 5, 6, 3), true
        assert_equal @numeric.twenty_one?(3, 11, 10), false
      end
    end
    

    Let's run the tests :-

    [arup@Ruby]$ ruby test/test_numerics.rb
    Run options: --seed 61602
    
    # Running:
    
    .
    
    Finished in 0.001332s, 750.9402 runs/s, 1501.8804 assertions/s.
    
    1 runs, 2 assertions, 0 failures, 0 errors, 0 skips
    [arup@Ruby]$
    

    In your method, it was returning Fixnum instance 21, which you were trying to compare with true. That's why you got the error. If you look at the source of assert_equal, you will find the comparisons between 2 objects which are the instances of the same class, otherwise it will throw the error you got.

    Note: You of-course can write this nums.inject(&:+) as nums.inject(:+) too as Ruby allows this freedom in case of #reduce/#inject method particularly out of the box.

    Update

    Carles Jove Buxeda gave one nice idea to design this problem. The idea is to put the methods inside the module, and then include it to test its methods:

    require "minitest/autorun"
    
    module Numerics
      def twenty_one?(*nums)
        nums.inject(:+) == 21
      end
    end
    
    class TestNumerics < MiniTest::Test
      include Numerics
    
      def test_twenty_one?
        assert_equal twenty_one?(3, 4, 5, 6, 3), true
        assert_equal twenty_one?(3, 11, 10), false
      end
    end
    

    Now if I run it :

    arup_ruby$ ruby test/test_numerics.rb
    Run options: --seed 1223
    
    # Running:
    
    .
    
    Finished in 0.001067s, 937.2071 runs/s, 1874.4142 assertions/s.
    
    1 runs, 2 assertions, 0 failures, 0 errors, 0 skips
    arup_ruby$
    

    Very cool idea!