Iteration - last detection

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?

···

Protect your PC - get McAfee.com VirusScan Online
http://clinic.mcafee.com/clinic/ibuy/campaign.asp?cid=3963

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?

That’s something I’ve been interested in.
Search for “super-iterator” in the ML archives.

I don’t know that there’s a solution in general
unless you build your own solution.

Actually, though, you can know the number of elements
in a hash just as you can an array (‘length’ or ‘size’
as a synonym, just as for arrays). If you don’t mind
keeping a counter, that works fine.

Hal

···

----- Original Message -----
From: “Orion Hunter” orion2480@hotmail.com
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Thursday, May 29, 2003 2:14 PM
Subject: Iteration - last detection

Orion Hunter wrote:

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?


Protect your PC - get McAfee.com VirusScan Online
Antivirus, VPN, Identity & Privacy Protection | McAfee

hmmm… I cannot find a really elegant solution to this… it would have
been nice to have some really good looking code solving this but sadly
you will have to make do with something along these lines:

some array (could have been a hash)

a = [3,4,5,6]

all but the last element

a[0,a.length-1].each { |n|

insert real code here

p n
}

the last element

p a[a.length]

hope this is satisfactory…

/Anders

···


dc -e
4ddod3dddn1-89danrn10-dan3+ann6dan2an13dn1+dn2-dn3+5ddan2/9+an13nap

Not built in, but this should work:

module Enumerable
def each_with_lastp # no shortage of nicer names, I’m sure :slight_smile:
flag = nil
i = nil
each {|j|
yield [i, false] if flag
i = j
flag = true
}
yield [i,true]
end
end

a = [1,2,3,4,5,6,7,8,9]
a.each_with_lastp {|i, last|
print i
print last ? “” : “,”
}

It’s rather crude, in that there’s the overhead of a call to ‘if flag’
with every iteration, but I couldn’t think of a way around that.

martin

···

Orion Hunter orion2480@hotmail.com wrote:

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?

Orion Hunter wrote:

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?

this might work for you, it has simplicity going for it.

module Enumerable
def each_but_last(&block)
elements = entries
last = elements.pop
elements.each &block
return last
end
end

last = %w{ one two three }.each_but_last { |i| puts “each_but_last #{i}” }
puts “last #{last}”

Dave

I couldn’t resist to post to this (so) long thread. It would be nice to
give an example why you should need this. So people answer with richer
ideas.
If ruby users == perl users this thread would be 1000 long :slight_smile:
Anyway, it’s becoming so hot here!
:slight_smile:

Orion Hunter wrote:

···

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?


Protect your PC - get McAfee.com VirusScan Online
Antivirus, VPN, Identity & Privacy Protection | McAfee

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

What about a method each_tuple(tuple_length) in Enumerable, that would be kind of a
generalization of each?. This should be O(container.length).

module Enumerable
def each_tuple(tuple_length)
tuple = [nil] * tuple_length

	each do |element|
		tuple = tuple[1..-1].push(element)
		yield(tuple)
	end
	(2..tuple_length).each do |i|
		tuple = tuple[1..-1].push(nil)
		yield(tuple)
	end
end

end

s = “”
[1,2,3,4,5,6,7,8,9,10].each_tuple(2) do |t|
next unless t[0]
s << (t[0].to_s + (t[1] ? “,” : “”))
end
puts s #gives “1,2,3,4,5,6,7,8,9,10”

[1,2,3,4,5,6,7,8,9,10].each_tuple(2) do |t|
puts t.inspect
end
#gives:
#[nil, 1]
#[1, 2]
#[2, 3]
#[3, 4]
#[4, 5]
#[5, 6]
#[6, 7]
#[7, 8]
#[8, 9]
#[9, 10]
#[10, nil]

greetings, Florian Pflug

···

On Fri, May 30, 2003 at 04:14:49AM +0900, Orion Hunter wrote:

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?

Apologies in advance :slight_smile:

def each_with_hack(&block)
i = nil
p = proc {|i, j| eval “p = block.to_proc”}
each {|j|
p.call(i, false)
i = j
}
p.call(i, true)
end

Seems to be about as fast (indeed, slower in some cases) than the flag
version, though - is proc.call significantly slower than yield?

martin

···

Orion Hunter orion2480@hotmail.com wrote:

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?

hmmm… I cannot find a really elegant solution to this… it would have
been nice to have some really good looking code solving this but sadly
you will have to make do with something along these lines:

some array (could have been a hash)

a = [3,4,5,6]

all but the last element

a[0,a.length-1].each { |n|

insert real code here

p n
}

the last element

p a[a.length]

With arrays you can also do this as

a[0…-2].each { |n|

}
p a.last

A little bit less typing. For a hash you can iterate in this manner over
Hash#keys.

···

hope this is satisfactory…

/Anders


dc -e
4ddod3dddn1-89danrn10-dan3+ann6dan2an13dn1+dn2-dn3+5ddan2/9+an13nap

you could use each_with_index to avoid the flag - but not th ‘if’:

module Enumerable
def each_with_last(&block)
l = nil
each_with_index do |e, i|
block.call l, false if i > 0
l = e
end
block.call l, true
end
end

btw. if you don’t mind me asking, why ‘yield [i,true]’? i remember some
posts awhile back about how params get passed to blocks and it was very
non-intuitive regarding list assignment and all…

-a

···

On Thu, 29 May 2003, Martin DeMello wrote:

Orion Hunter orion2480@hotmail.com wrote:

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?

Not built in, but this should work:

module Enumerable
def each_with_lastp # no shortage of nicer names, I’m sure :slight_smile:
flag = nil
i = nil
each {|j|
yield [i, false] if flag
i = j
flag = true
}
yield [i,true]
end
end

a = [1,2,3,4,5,6,7,8,9]
a.each_with_lastp {|i, last|
print i
print last ? “” : “,”
}

It’s rather crude, in that there’s the overhead of a call to ‘if flag’
with every iteration, but I couldn’t think of a way around that.

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’
====================================

Here is a module that implements it, but you should include it in every
[…]

Hmmm… this should handle the #each nesting properly (but still has
problems with last, break, etc, I think):

— oe.rb~ 2003-05-30 12:56:28.000000000 +0200
+++ oe.rb 2003-05-30 14:00:52.000000000 +0200
@@ -18,6 +18,7 @@
alias_method :#{old_each}, :each

		def each(*a,&b)
  •   		old_last=last?
      		element = nil
      		flag = false
      		#{old_each}(*a) { |e|
    

@@ -25,6 +26,9 @@
@last_iteration=false
}
b.call element if flag

  •   			Thread.current.instance_eval {
    
  •   				@__last_iteration__=old_last
    
  •   			}
      			flag=true
      			element=e
      		}
    

@@ -34,7 +38,7 @@
}
b.call element
Thread.current.instance_eval {

  •   				@__last_iteration__=false
    
  •   				@__last_iteration__=old_last
      			}
      		end
      	end
    

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

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

   def last?
      # ...
   end

is faster to write :slight_smile:

        #{old_each}(*a) { |e|
          Thread.current.instance_eval {
            @__last_iteration__=false
          }

ruby has thread local variable, you can write it

          Thread.current['__last_iteration__'] = false

class Array; include EachWithLast end

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

unfortunately it will not work with 1.8, Array don't use #each for #select
in 1.8

Guy Decoux

svg% cat b.rb
#!./ruby
def last?
   Thread.current['__last__']
end

module EachWithLast
   def each:hook
      element = nil
      flag = false
      super do |e|
         Thread.current['__last__'] = false
         yield element if flag
         flag = true
         element = e
      end
      if flag
         Thread.current['__last__'] = true
         yield element
         Thread.current['__last__'] = false
      end
   end
end

class Array; include EachWithLast end

a = [1,2,3,4,5].each {|e| p "received #{e} #{last?}" }

svg%

svg% b.rb
"received 1 false"
"received 2 false"
"received 3 false"
"received 4 false"
"received 5 true"
svg%

Is there any built in functionality for iteration that will allow me to
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.

···

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

I like that this solution lacks a flag to indicate whether I’m on the
last element; this makes it easy to use.

I don’t like that all the elements are stored in a temporary array.

Combining this with Martin’s idea, I get:

module Enumerable
def each_but_last
prev = nil
curr = nil
each do |curr|
yield prev if prev
prev = curr
end
return prev
end
end

It uses less memory, but:

  1. It’s surprisingly slower
  2. Both solutions have the problem of not being able to distinguish
    between and [nil].

Paul

···

On Fri, May 30, 2003 at 06:12:38AM +0900, Dave wrote:

this might work for you, it has simplicity going for it.

module Enumerable
def each_but_last(&block)
elements = entries
last = elements.pop
elements.each &block
return last
end
end

last = %w{ one two three }.each_but_last { |i| puts “each_but_last #{i}” }
puts “last #{last}”

I like Martin’s implementation. I’ve modified it so that it handles an empty
collection.

···

#–
module Enumerable
def each_with_last
prev = nil
empty = true
each { |item|
if empty
empty = false
else
yield prev,false
end
prev = item
}
if not empty
yield prev,true
end
end
end

def format(a)
result = "["
a.each_with_last {|i, last|
result += i.to_s
if not last
result += ","
end
}
result += "]"
end

puts(format([1,2,3,4,5,6,7,8,9]))
puts(format([1,2]))
puts(format([1]))
puts(format([]))
#–

Since the flag variable (‘empty’ here) is necessary to detect the empty
collection, the use of each_with_index as in other posts was unnecessary.

Steve.

It’s rather crude, in that there’s the overhead of a call to ‘if flag’
with every iteration, but I couldn’t think of a way around that.

you could use each_with_index to avoid the flag - but not th ‘if’:

That isn’t much better, though - you have the overhead of generating an
index you never use. It’s annoying because the test is guaranteed to
only succeed right at the beginning, but you nonetheless have to carry
it all the way through. I was even thinking of self-modifying code to
get around that :slight_smile: I suspect that in real terms it’s a negligible cost,
but it’s still inelegant.

module Enumerable
def each_with_last(&block)
l = nil
each_with_index do |e, i|
block.call l, false if i > 0
l = e
end
block.call l, true
end
end

btw. if you don’t mind me asking, why ‘yield [i,true]’? i remember some
posts awhile back about how params get passed to blocks and it was very
non-intuitive regarding list assignment and all…

Hm - for some reason I thought yield had to yield a single value. Too
late at night :slight_smile:

martin

···

ahoward ahoward@fsl.noaa.gov wrote:

On Thu, 29 May 2003, Martin DeMello wrote:

Hi –

module Enumerable
def each_with_last(&block)
l = nil
each_with_index do |e, i|
block.call l, false if i > 0
l = e
end
block.call l, true
end
end

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

(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:

David

···

On Fri, 30 May 2003, ahoward wrote:


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

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

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

David

···

On Fri, 30 May 2003, ahoward wrote:

module Enumerable
def each_with_last(&block)
l = nil
each_with_index do |e, i|
block.call l, false if i > 0
l = e
end
block.call l, true
end
end


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