Dire need for inject_with_index

Hi people!

See my nifty little class for converting a few time strings to
seconds. See how the last line of self.seconds reverses an array and
then adds up each part, multiplying the first with 1, the second with
60 and the third with 60*60. So 1:25:20 is a total of one hour, 25
minutes and 20 seconds - all converted to seconds.

Now look how I have to make a magical auto-incrementing counter
variable because inject doesn’t provide the element index. Is there a
better way than this? Should I write an inject_with_index?

Les

Seconder.seconds parses time formats and converts them to seconds,

returning a string representation. It accepts:

10:25:05

17d (17 days)

10h (10 hours)

25m

5s

25:05

5

class Seconder
DURATIONS = {‘s’ => 1, ‘m’ => 60, ‘h’ => 6060, ‘d’ => 6060*24}

def self.count
@counter += 1
end

def self.seconds(string)
if string !~
/^\d+$|^\d{1,2}:\d{1,2}$|^\d{1,2}:\d{1,2}:\d{1,2}$|^\d+s$|^\d+h$|^\d+m$|^\d+d$/
return “Format incorrect, try 10:25:05 for hh:mm:ss or 10h for 10
hours”
end

lastChar = string[-1,1]
if DURATIONS.has_key?(lastChar) && string.length > 1

  firstChars = string[0..-2]
  return (firstChars.to_i * DURATIONS[lastChar]).to_s
end

@counter = 0
string.split(":").reverse.map{|part| part.to_i}.inject{|sum, part|

sum + part * 60 ** count}
end
end

Hope this helps:

require “enumerator”
=> true

module Enumerable
def inject_with_index(*args, &block)
enum_for(:each_with_index).inject(*args, &block)
end
end
=> nil

(“A”…“D”).inject_with_index("") do |str, (let, i)|
?> i % 2 == 0 ? str += let : str

end
=> “AC”

James Edward G. II

On 8/12/06, James Edward G. II [email protected] wrote:

(“A”…“D”).inject_with_index(“”) do |str, (let, i)|
?> i % 2 == 0 ? str += let : str
end
=> “AC”

That most certainly does!
Les

On 8/12/06, Leslie V. [email protected] wrote:

=> nil

(“A”…“D”).inject_with_index(“”) do |str, (let, i)|
?> i % 2 == 0 ? str += let : str
end
=> “AC”

What I don’t understand about your solution is the “do |str, (let, i)|”,
how can you have brackets in there? What does that do?

Your example works as above but I can’t get it to work with an array.
Here’s what I get:

p [1,3,5].inject_with_index{|sum, (part, index)| sum + part}
=> seconderc.rb:46:in `+': can’t convert Fixnum into Array (TypeError)

p [1,3,5].inject_with_index{|sum, part, index| sum + part}
=> [1, 0, 3, 1, 5, 2]

My own inject is a lot clunkier (and takes no params) but seems to work:

def inject_with_index
total = self[0]
index = 1
loop do
break if index == self.length
total = yield(total, self[index], index)
index += 1
end
total
end
public :inject_with_index

p [1,3,5].inject_with_index{|sum, part, index| sum + part}

=> 9

Les

On Aug 12, 2006, at 3:22 PM, Leslie V. wrote:

end
=> nil

(“A”…“D”).inject_with_index("") do |str, (let, i)|
?> i % 2 == 0 ? str += let : str

end
=> “AC”

What I don’t understand about your solution is the “do |str, (let,
i)|”,
how can you have brackets in there? What does that do?

I used the parens to split the arguments, as you can in Ruby
assignments.

Your example works as above but I can’t get it to work with an array.
Here’s what I get:

p [1,3,5].inject_with_index{|sum, (part, index)| sum + part}
=> seconderc.rb:46:in `+’: can’t convert Fixnum into Array (TypeError)

This expression uses inject’s default behavior to setup sum, but
remember that elements are now paired with their index. sum is thus
initialized to [1, 0], which blows up your later math. You can fix
this by initializing sum yourself:

[1,3,5].inject_with_index(0) {|sum, (part, index)| sum + part}

James Edward G. II