Iteration - last detection

I may be over simplifiying, but Enumerable defines max, so couldn’t you:

a = {}
a[“a”] = “A”
a[“b”] = “B”
a[“c”] = “C”
a.each { |key, val|
print “#{key} = #{val}”
if key === a.keys.max
puts " and that’s the last one"
else
puts
end
}

Regards,
JJ

···

on 5/30/03 8:08 AM, Robert at bob.news@gmx.net wrote:

Detecting if the first element might in some circumstances server the same
purpose and it’s easy done without Thread tricks:

def print_format(enum)
first = true
str = “[”

enum.each do |e|
if first
first = false
else
str << ", "
end

str << e.to_s
end

str << “]”
str
end

a=[10, 30, “foo”]
puts print_format(a)

robert

“Carlos” angus@quovadis.com.ar schrieb im Newsbeitrag
news:20030530113250.GB1403@quovadis.com.ar…

Is there any built in functionality for iteration that will allow me to
detect when I am on the last element? This would not be hard for an
Array,
using array.length, but what about a Hash? How would I know when I am
at
the last element?

Here is a module that implements it, but you should include it in every
class where you want #each to behave that way :-/. (And some #each-like
methods (for example, String#each_byte, don’t work at all…)

oe.rb:
#!/usr/bin/ruby

class Object
def last?
Thread.current.last_iteration?
end
private :last?
end

class Thread
def last_iteration?
@last_iteration
end
end

module EachWithLast
def EachWithLast.append_features(klass)
old_each=“each_”+rand(10000).to_s+Time.now.usec.to_s
klass.class_eval <<-FINISH
alias_method :#{old_each}, :each

def each(*a,&b)
element = nil
flag = false
#{old_each}(*a) { |e|
Thread.current.instance_eval {
@last_iteration=false
}
b.call element if flag
flag=true
element=e
}
if flag
Thread.current.instance_eval {
@last_iteration=true
}
b.call element
Thread.current.instance_eval {
@last_iteration=false
}
end
end
FINISH
end
end

class Array; include EachWithLast end

a = [1,2,3,4,5].select {|e| last? }
p a # => [5]

class Range; include EachWithLast end
class String; include EachWithLast end

for i in 1…10
print “and the last is: " if last?
print i
“ds\njkd\njsk\nsd”.each { |l| puts " - the last line is #{l}” if last? }
end

class File; include EachWithLast end
File.open(“oe.rb”) { |f|
count = 0
f.each { |line|
count += line.length
print “last line: #{line}” if last?
}
print “total chars: #{count}\n”
}
#THE LAST LINE

Thanks for advice. But this subject is no longer important; what is
important now is…

def each:hook
super do |e|

When will be this in Ruby??? :))

Err??!

I must be missing something; why is this not an incredibly simple thing to
achieve?

module Enumerable
def each_with_last

The methods in Enumerable don’t call #each_with_last, they call
#each… But now it seems that they don’t call #each either :-/.

Also, “if prev yield prev” fails if there’s a nil or a false in the
array. That’s why you need (afaics) an explicit flag to say ‘this is the
first iteration; prev has no value’. I tried leaving prev undefined and
rescuing NameError instead, but then the value doesn’t persist beyond
the block.

martin

···

Paul Brannan pbrannan@atdesk.com wrote:

  1. Both solutions have the problem of not being able to distinguish
    between and [nil].

Great solution. Thank’s for showing how simple some things become if you
look at them from the right perspective.

robert

“Brian Candler” B.Candler@pobox.com schrieb im Newsbeitrag
news:20030530123215.GA8185@uk.tiscali.com

Is there any built in functionality for iteration that will allow me
to

···

On Fri, May 30, 2003 at 08:33:15PM +0900, Carlos wrote:

detect when I am on the last element?

Here is a module that implements it, but you should include it in every
class where you want #each to behave that way :-/.

def EachWithLast.append_features(klass)
old_each=“each_”+rand(10000).to_s+Time.now.usec.to_s
klass.class_eval <<-FINISH

Err??!

I must be missing something; why is this not an incredibly simple thing to
achieve?

module Enumerable
def each_with_last
prev = nil
each_with_index do |item,index|
yield prev,false if index > 0
prev = item
end
yield prev,true
end
end

irb(main):011:0> [1,2,3,4,5].each_with_last do |a,b| puts a,b; end
1
false
2
false
3
false
4
false
5
true

It was Nobu’s comment that this should work with IO which made me think it
must work this way: you simply keep reading items until there are no more
(eof in the case of IO), and then you yield with ‘true’ for the last one.

But like I say, I might have missed something…

Regards,

Brian.

dblack@superlink.net wrote:

Let Ruby do more of the work for you :slight_smile:

module Enumerable
def each_with_last
each_with_index {|e,i| yield(e, i == size-1)}
end
end

You aren’t guaranteed ‘size’, though.

martin

Hmmm… Too much work isn’t good either. If Enumerable#size
is defined in terms of #each your iteration is in O(n ** 2).

This would fix that problem:

module Enumerable
def each_with_last
size_minus_1 = size - 1
each_with_index { |e,i| yield(e, i == size_minus_1) }
end
end

···

On 2003-05-30 06:46:42 +0900, dblack@superlink.net wrote:

Let Ruby do more of the work for you :slight_smile:

module Enumerable
def each_with_last
each_with_index {|e,i| yield(e, i == size-1)}
end
end


Faith is that quality which enables us to believe what we know to be
untrue.

Let Ruby do more of the work for you :slight_smile:

module Enumerable
def each_with_last
each_with_index {|e,i| yield(e, i == size-1)}
end
end

this won’t work for anything Enumerable though - though it works for Array
and Hash.

“The Enumerable mixin provides collection classes with several traversal and
searching methods, and with the ability to sort. The class must provide a
method each, which yields successive members of the collection. If
Enumerable#max , #min, or #sort is used, the objects in the collection must
also implement a meaningful <=> operator, as these methods rely on an ordering
between members of the collection.”

so you may or may not have a size method.

(Hmmm… could this be the non-superfluous use of Enumerable#each_with_index
(as opposed to just Array#ewi) that I’ve been looking for for 2.5 years? :slight_smile:

you don’t like it? i use it all the time! :wink:

-a

···

On Fri, 30 May 2003 dblack@superlink.net wrote:

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ara.t.howard@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
~ > ruby -e ‘p % ^) .intern’
====================================

Hi,

···

At Fri, 30 May 2003 07:58:35 +0900, dblack@superlink.net wrote:

OK, as penance for clouding the issue with the mythical
Enumerable#size, I feel morally obliged to write a real version :slight_smile:
I’m not sure how horrible this is. Very, I suspect. But anyway,
another possibility:

module Enumerable
def each_with_last
n = -1
each { n += 1 }
each_with_index {|e,i| yield(e, i == n)}
end
end

Enumerable doesn’t guarantee multiple iterations. e.g. IO.


Nobu Nakada

dblack@superlink.net wrote:

module Enumerable
def each_with_last
n = -1
each { n += 1 }
each_with_index {|e,i| yield(e, i == n)}
end
end

It benchmarks a bit faster than yours, for arrays of size 10 to 10000,
but not wildly so.

My flag version benchmarks fater still

user system total real
flag: 146.570000 0.270000 146.840000 (167.307243)
index: 190.220000 1.170000 191.390000 (219.334280)

Also, if we’re really bumming cycles (and depending on how differently
you want to treat the last element), you could yield 0…n-1 and return
n, saving the two-valued yield and the test in the client method.

martin

I don’t follow. The pickaxe says:

module Enumerable
Relies on: each, <=>

The Enumerable mixin provides collection classes with several
traversal and searching methods, and with the ability to sort. The
class must provide a method each, which yields successive members of
the collection.

So they only rely on the existence of ‘each’, unless you are using
sort/min/max.

I thought the question was “how do I get a method like ‘each’, but which
includes a flag saying whether this is the last element or not?” In which
case, you just define another method which does that, called (say)
‘each_with_last’, which makes use of ‘each’. Where does this fail?

Cheers,

Brian.

···

On Fri, May 30, 2003 at 10:09:39PM +0900, Carlos wrote:

I must be missing something; why is this not an incredibly simple thing to
achieve?

module Enumerable
def each_with_last

The methods in Enumerable don’t call #each_with_last, they call
#each… But now it seems that they don’t call #each either :-/.

John Johnson wrote:

I may be over simplifiying, but Enumerable defines max, so couldn’t you:

a = {}
a[“a”] = “A”
a[“b”] = “B”
a[“c”] = “C”
a.each { |key, val|
print “#{key} = #{val}”
if key === a.keys.max
puts " and that’s the last one"
else
puts
end
}

that would only work if the array is sorted…

···

Regards,
JJ

on 5/30/03 8:08 AM, Robert at bob.news@gmx.net wrote:

Detecting if the first element might in some circumstances server the same
purpose and it’s easy done without Thread tricks:

def print_format(enum)
first = true
str = “[”

enum.each do |e|
if first
first = false
else
str << ", "
end

str << e.to_s
end

str << “]”
str
end

a=[10, 30, “foo”]
puts print_format(a)

robert

“Carlos” angus@quovadis.com.ar schrieb im Newsbeitrag
news:20030530113250.GB1403@quovadis.com.ar…

Is there any built in functionality for iteration that will allow me to
detect when I am on the last element? This would not be hard for an

Array,

using array.length, but what about a Hash? How would I know when I am

at

the last element?

Here is a module that implements it, but you should include it in every
class where you want #each to behave that way :-/. (And some #each-like
methods (for example, String#each_byte, don’t work at all…)

oe.rb:
#!/usr/bin/ruby

class Object
def last?
Thread.current.last_iteration?
end
private :last?
end

class Thread
def last_iteration?
@last_iteration
end
end

module EachWithLast
def EachWithLast.append_features(klass)
old_each=“each_”+rand(10000).to_s+Time.now.usec.to_s
klass.class_eval <<-FINISH
alias_method :#{old_each}, :each

def each(*a,&b)
element = nil
flag = false
#{old_each}(*a) { |e|
Thread.current.instance_eval {
@last_iteration=false
}
b.call element if flag
flag=true
element=e
}
if flag
Thread.current.instance_eval {
@last_iteration=true
}
b.call element
Thread.current.instance_eval {
@last_iteration=false
}
end
end
FINISH
end
end

class Array; include EachWithLast end

a = [1,2,3,4,5].select {|e| last? }
p a # => [5]

class Range; include EachWithLast end
class String; include EachWithLast end

for i in 1…10
print “and the last is: " if last?
print i
“ds\njkd\njsk\nsd”.each { |l| puts " - the last line is #{l}” if last? }
end

class File; include EachWithLast end
File.open(“oe.rb”) { |f|
count = 0
f.each { |line|
count += line.length
print “last line: #{line}” if last?
}
print “total chars: #{count}\n”
}
#THE LAST LINE


dc -e 4ddod3ddn1-0nn1dnd+nn3dn1+n3*1+ddn1+dn2-n5dn1+dnrn1-p | tr
0123456 yorh@k. | dd conv=lcase

Hi –

···

On Fri, 30 May 2003, Martin DeMello wrote:

dblack@superlink.net wrote:

Let Ruby do more of the work for you :slight_smile:

module Enumerable
def each_with_last
each_with_index {|e,i| yield(e, i == size-1)}
end
end

You aren’t guaranteed ‘size’, though.

Yikes. So much for conciseness :slight_smile: Thanks for the timely correction.

David


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

I don't follow.

In 1.8 Array#select don't call Array#each

Guy Decoux

Ah yes, this is true.

Thanks!

···

on 5/30/03 2:13 PM, Anders Borch at spam@deck.dk wrote:

John Johnson wrote:

I may be over simplifiying, but Enumerable defines max, so couldn’t you:

a = {}
a[“a”] = “A”
a[“b”] = “B”
a[“c”] = “C”
a.each { |key, val|
print “#{key} = #{val}”
if key === a.keys.max
puts " and that’s the last one"
else
puts
end
}

that would only work if the array is sorted…


Regards,
JJ

Be Kind, Be Careful, Be Yourself

More precisely, it only works if the array (or other collection) elements
have a defined <=> method which can compare them.

irb(main):010:0> [1,5,3,2,1].max
=> 5

However that’s still no good, as it involves iterating over the collection a
second time. For something like an IO object, attached to a pipe which can’t
be rewound, you can’t iterative over it twice.

Regards,

Brian.

···

On Sat, May 31, 2003 at 03:13:16AM +0900, Anders Borch wrote:

John Johnson wrote:

I may be over simplifiying, but Enumerable defines max, so couldn’t you:

a = {}
a[“a”] = “A”
a[“b”] = “B”
a[“c”] = “C”
a.each { |key, val|
print “#{key} = #{val}”
if key === a.keys.max
puts " and that’s the last one"
else
puts
end
}

that would only work if the array is sorted…

Did someone propose implementing each_with_last in terms of select? (I don’t
see how this could be done)

Regards,

Brian.

···

On Fri, May 30, 2003 at 11:32:56PM +0900, ts wrote:

I don’t follow.

In 1.8 Array#select don’t call Array#each

Did someone propose implementing each_with_last in terms of select?

see [ruby-talk:72510]

Guy Decoux

Oh OK - make ‘last?’ a sort of pseudo-instance variable rather than passing
it as a parameter to each_with_last. Looks messy, as it splits the iterator
state: some carried by the iterator, and some carried outside it.

Cheers,

Brian.

···

On Sat, May 31, 2003 at 12:09:00AM +0900, ts wrote:

Did someone propose implementing each_with_last in terms of select?

see [ruby-talk:72510]

Oh OK - make 'last?' a sort of pseudo-instance variable rather than passing
it as a parameter to each_with_last. Looks messy, as it splits the iterator
state: some carried by the iterator, and some carried outside it.

But you don't solve the problem, in [ruby-talk:72510] you have

class Array; include EachWithLast end
a = [1,2,3,4,5].select {|e| last? }
p a # => [5]

1.8 with not call the method EachWithLast#each

Guy Decoux