[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Dire need for inject_with_index

Leslie Viljoen

8/11/2006 10:58:00 PM

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' => 60*60, 'd' => 60*60*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

4 Answers

James Gray

8/11/2006 11:27:00 PM

0

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 Gray II

Leslie Viljoen

8/12/2006 8:02:00 AM

0

On 8/12/06, James Edward Gray II <james@grayproductions.net> wrote:
> 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"

That most certainly does!
Les

Leslie Viljoen

8/12/2006 8:23:00 PM

0

On 8/12/06, Leslie Viljoen <leslieviljoen@gmail.com> wrote:
> On 8/12/06, James Edward Gray II <james@grayproductions.net> wrote:
> > 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"

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

James Gray

8/12/2006 8:41:00 PM

0

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

> On 8/12/06, Leslie Viljoen <leslieviljoen@gmail.com> wrote:
>> On 8/12/06, James Edward Gray II <james@grayproductions.net> wrote:
>> > 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"
>
> 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 Gray II