Ruby: combining methods into 1 line

Viewed 84

I have an array of objects, and I want to take one of the methods to return it into 1 single line.

here is my array

def set
        @seconds = Counter.new('Seconds')
        @minutes = Counter.new('Minutes')
        @hours = Counter.new('Hours')

        @time = [@seconds, @minutes, @hours]
    end

and here is the method I'm trying to print in the Counter class

    def count
        @counts
    end

Here's my attempt

    def read
        reverse = @time.reverse
        @time.each do |t|
            return '%02d' %t.count.to_s.join(":")
        end
    end

(I also want them to be in 00, 01 formats, hence the '%02d')

.join doesn't seem to work (I'm guessing it works with arrays but not methods of an array?

any help will be appreciated!

2 Answers

Given this Counter class and some instances:

class Counter
  attr_accessor :count

  def initialize(name, count: 0)
    @name = name
    @count = count
  end
end

@seconds = Counter.new('Seconds', count: 52)
@minutes = Counter.new('Minutes', count: 30)
@hours = Counter.new('Hours', count: 11)

@time = [@seconds, @minutes, @hours]

You can extract the count values in hour / minute / second order via:

@time.reverse.map(&:count)
#=> [11, 30, 52]

This array can be passed (in its entirety) to String#% to produce a HH:MM:SS string:

'%02d:%02d:%02d' % @time.reverse.map(&:count)
#=> "11:30:52"

Some improvements

You can get rid of reverse by changing the order of your @time array:

@time = [@hours, @minutes, @seconds]

'%02d:%02d:%02d' % @time.map(&:count)
#=> "11:30:52"

This can be simplified further by providing a to_i method:

class Counter
  # ...

  def to_i
    @count
  end
end

# ...

@time = [@hours, @minutes, @seconds]

'%02d:%02d:%02d' % @time
#=> "11:30:52"

The above works because % tries to call to_i when specifying an integer format.

You're very close!

You need to do the joining outside of your loop, and use collect instead of each. Here is an irb session that should help you figure things out:

>> seconds = 35
>> minutes = 9
>> hours = 11
>> time = [seconds, minutes, hours]
>> time.reverse.collect{|t| "%02d" % t}.join(":")
=> "11:09:35"

The idea is that collect (called map in other languages) applies the stringification operation to each array element, turning

[11, 9, 35]

into

["11", "09", "35"]

then you can do the join on this array.

Related