Each_with_index & collect_with_index?

Array.each (and others) have an alternative .each_index which passes the index
to the element instead of the element itself to the block. Is there an easy
way to pass both the index and the element, eg something like
Array.each_with_index{ |i, el| … }
Also, an equivalent for these in ‘collect’ could be useful, eg
Array.collect_index{ |i| … }
Array.collect_with_index{ |i, el| … }

How about it?

Tim Bates

···


tim@bates.id.au

Hi,

Array.each (and others) have an alternative .each_index which passes the index
to the element instead of the element itself to the block. Is there an easy
way to pass both the index and the element, eg something like
Array.each_with_index{ |i, el| … }

We already have this one.

Also, an equivalent for these in ‘collect’ could be useful, eg
Array.collect_index{ |i| … }
Array.collect_with_index{ |i, el| … }

These ideas are proposed several times. But no one ever succeeded to
persuade me to add these. Do you think of any concrete usage of these
methods, not just for seeking completeness?

						matz.
···

In message “each_with_index & collect_with_index?” on 02/11/26, Tim Bates tim@bates.id.au writes:

[snip]

Array.collect_index{ |i| … }
[snip]

a = [42, ‘forty-two’]

(0…a.size).collect { |idx| idx} >> [0, 1]

not too bad…

[snip]

Array.collect_with_index{ |i, el| … }
[snip]

a = [42, ‘forty-two’]

(0…a.size).collect { |i| [i,a[i]] } >> [[0, 42], [1, “forty-two”]]

i can see your point. :wink:

-a

···

On Tue, 26 Nov 2002, Tim Bates wrote:

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

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

Hi –

Array.each (and others) have an alternative .each_index which passes the index
to the element instead of the element itself to the block. Is there an easy
way to pass both the index and the element, eg something like
Array.each_with_index{ |i, el| … }

Yes, there is such a method. Your notation is a bit off, because it
looks like a class method of Array instead of an instance method of
arrays (actually of enumerables), and the element comes first:

ar.each_with_index {|el,i| … }

but anyway, it exists.

Also, an equivalent for these in ‘collect’ could be useful, eg
Array.collect_index{ |i| … } Array.collect_with_index{ |i, el|
… } How about it?

The first one can be done with:

(0…array.size).map {|i| … }

though it’s not symmetrical as to the wording.

As for the second… I recently appointed myself President of
Citizens for MWI [map_with_indices], Inc. (See
http://www.ruby-talk.org/51619). You may join the organization,
free of charge :slight_smile: Anyway, there’s been a lot of talk of this at
various points; if you search for map_with_index and map_with_indices
on the ruby-talk archive, you’ll see most of it. Matz has recently
indicated that he doesn’t think there’s a demand or need for it. I’m
still trying to keep the dream alive :slight_smile:

David

···

On Tue, 26 Nov 2002, Tim Bates wrote:


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

I gave one example in another thread, which was the example that I ran into
that caused me to bring this up in the first place.

To take a string, split it into paragraphs, turn each of them into an HTML


and number them:

i = 0
string.split(“\n”).collect{ |s| ‘

’ + (i += 1).to_s + " - " + s }.join(“\n”)

could be better written as:

string.split(“\n”).collect_with_index{ |i, s| ‘

’ + i.to_s + " - " + s
}.join(“\n”)

removing the need for the external ‘i’ variable. However, is seeking
completeness such a horrible thing? We have an each_with_index, and the
parallels between each and collect are enough that one might expect there to
be an equivalent for collect.

Tim Bates

···

On Tue, 26 Nov 2002 02:13 pm, Yukihiro Matsumoto wrote:

These ideas are proposed several times. But no one ever succeeded to
persuade me to add these. Do you think of any concrete usage of these
methods, not just for seeking completeness?


tim@bates.id.au

dblack@candle.superlink.net wrote:

As for the second… I recently appointed myself President of
Citizens for MWI [map_with_indices], Inc. (See
http://www.ruby-talk.org/51619). You may join the organization,
free of charge :slight_smile: Anyway, there’s been a lot of talk of this at
various points; if you search for map_with_index and map_with_indices
on the ruby-talk archive, you’ll see most of it. Matz has recently
indicated that he doesn’t think there’s a demand or need for it. I’m
still trying to keep the dream alive :slight_smile:

How about a wiki page of uses for it? I keep thinking it’s a nifty
feature, and then not being able to think of where I’d actually use it.

Okay, here's one...

a.map_with_index {|e,i| “#{e}\t#{i.mod(4).zero? ? “\n”:”"}.join

4 column printing

martin

As for the second… I recently appointed myself President of
Citizens for MWI [map_with_indices], Inc. (See
http://www.ruby-talk.org/51619). You may join the organization,
free of charge :slight_smile: Anyway, there’s been a lot of talk of this at
various points; if you search for map_with_index and map_with_indices
on the ruby-talk archive, you’ll see most of it. Matz has recently
indicated that he doesn’t think there’s a demand or need for it. I’m
still trying to keep the dream alive :slight_smile:

Doing numerically oriented algorithms I find myself frequently doing
something like the following:

z = (1…5).to_a
a = (0…5).to_a
b = (0…5).to_a
c = z.collect_with_index { |el, i| f(el, a[i], b[i]) }

where f is some arbitrary function.

For the most part I have to create c as an empty array and push elements
on to the array as I’m doing z.each_index. The use of some sort of list
comprehension-ish type of thing would be suitable as well, maybe
something along the lines of a zip.

z =
a = as before
b =
c = zip (z, a, b).collect { |el| f(el[0], el[1], el[2]) }

Though I think this was the discussion in another thread.

···


Gordon James Miller Bit Twiddlers, Inc
Software Architecture Architect gmiller@bittwiddlers.com
(571)330-9012

Hi,

i = 0
string.split(“\n”).collect{ |s| ‘

’ + (i += 1).to_s + " - " + s }.join(“\n”)

could be better written as:

string.split(“\n”).collect_with_index{ |i, s| ‘

’ + i.to_s + " - " + s
}.join(“\n”)

I prefer this

require ‘stringio’

s = StringIO.new
string.each_with_index do |line, i|
s.puts “

#{i} - #{line}”
end

However, is seeking completeness such a horrible thing?

No. It’s not bad. It’s not horrible. It’s just not a ultimate goal.

						matz.
···

In message “Re: each_with_index & collect_with_index?” on 02/11/26, Tim Bates tim@bates.id.au writes:

[snip]

c = z.collect_with_index { |el, i| f(el, a[i], b[i]) }
[snip]

c = collect %w(0 1 2), %w(zero one two) do |a, b|
[a,b]
end

p c >> [[“0”, “zero”], [“1”, “one”], [“2”, “two”]]

c = collect_with_index %w(0 1 2), %w(zero one two) do |idx, a, b|
[idx, a,b]
end

p c >> [[0, “0”, “zero”], [1, “1”, “one”], [2, “2”, “two”]]

imho, this syntax is cleaner.

check out :

http://eli.fsl.noaa.gov/lib/ruby/site_ruby/1.6/enumerable.rb

for the source code.

i got the iterator class from Pit Capitain pit@capitain.de, it seems very
efficient. thanks Pit!

this allowed me to write Kernel#each (*enumerables, &block), Kernel#collect
(*enumerables, &block), Kernel#collect_with_index (*enumerables, &block)
very easily. you could write ‘zip’ if you wanted to in around 10 lines.
personally i like the Kernel# namespace for these methods but i realize this
is a religious issue.

-a

···

On Wed, 27 Nov 2002, Gordon Miller wrote:

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

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

I’m also always amazed by the uses of inject. The collect_with_index
could be done:

c = z.inject( ) { |arr, el|
arr << f( el, a[arr.length], b[arr.length] )
}

Actually, I think an each_with_index equivalent is kinda neat
with inject, as you can explicitly set the first index:

arr =
z.inject( 0 ) { |i, el|
arr[i] = f(el, a[i], b[i])
i + 1
}

It would be really cool if, instead of having a million *_with_index
methods floating around in Enumerable, we had a method which could be
used inside a block to retrieve the number of iterations that the block
has undergone.

For instance:

class Array
def collect( &e )
ary =
i = 0
self.each { |item|
eval( “def index; #{i} end”, e )
ary.push( e.call( item ) )
i += 1
}
return ary
end
end

The `index’ method can then be used in the block:

c = z.collect { |el| f(el, a[index], b[index]) }

This implementation is flawed, though, because it creates the `index’
method in whatever namespace the block is originally placed. But, you
know… what if??

_why

···

Gordon Miller (gmiller@promisemark.com) wrote:

Doing numerically oriented algorithms I find myself frequently doing
something like the following:

z = (1…5).to_a
a = (0…5).to_a
b = (0…5).to_a
c = z.collect_with_index { |el, i| f(el, a[i], b[i]) }

something along the lines of a zip…

c = zip (z, a, b).collect { |el| f(el[0], el[1], el[2]) }

Hi –

[snip]

c = z.collect_with_index { |el, i| f(el, a[i], b[i]) }
[snip]

c = collect %w(0 1 2), %w(zero one two) do |a, b|
[a,b]
end

p c >> [[“0”, “zero”], [“1”, “one”], [“2”, “two”]]

c = collect_with_index %w(0 1 2), %w(zero one two) do |idx, a, b|
[idx, a,b]
end

p c >> [[0, “0”, “zero”], [1, “1”, “one”], [2, “2”, “two”]]

imho, this syntax is cleaner.

It could get unclean in a hurry, though :slight_smile:

class Thing < Array
def meth(other)
a = collect { |e| do_something_with(e) }
b = collect_with_index(self,other)… # ugh

end
end

David

···

On Wed, 27 Nov 2002, ahoward wrote:

On Wed, 27 Nov 2002, Gordon Miller wrote:


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

It would be really cool if, instead of having a
million *_with_index
methods floating around in Enumerable, we had a
method which could be
used inside a block to retrieve the number of
iterations that the block
has undergone.

For instance:

class Array
def collect( &e )
ary =
i = 0
self.each { |item|
eval( “def index; #{i} end”, e )
ary.push( e.call( item ) )
i += 1
}
return ary
end
end

The `index’ method can then be used in the block:

c = z.collect { |el| f(el, a[index], b[index]) }

This implementation is flawed, though, because it
creates the `index’
method in whatever namespace the block is originally
placed. But, you
know… what if??

_why

(Bear in mind… I’m a newbie :wink:

Wow… I like this… or at least an slight variation.

Why not have a global var (eg $# or something) that
keeps the current index whenever you’re iterating
through an array/string/whatever?

It would be consistent, extremely useful, less
confusing and completely straightforward.

OK… let the criticism flow!

Jason

[snip]

It could get unclean in a hurry, though :slight_smile:

class Thing < Array
def meth(other)
a = collect { |e| do_something_with(e) }
b = collect_with_index(self,other)… # ugh

end
end
[snip]

i’m unclear as to what you mean :

a = collect { |e| do_something_with(e) }

this would be Array#collect, what does it have to do with the following line?

b = collect_with_index(self,other)…

i don’t really understand to point of the example.

and why ‘ugh’? you have to have, at minimum, one method name and a name for
each Enumerable in the argument list : how could it get any more succinct than
that? if you have a better suggestion i can incorporate it and post it…

on second thought i would like to retract earlier statements about those
methods belonging in Kernel (what was i thinking ) and say those methods
should go in Enumerable as module methods so the above would need be

b = Enumerable.collect_with_index(self,other)…

i’ve updated

http://eli.fsl.noaa.gov/lib/ruby/site_ruby/1.6/enumerable.rb

to reflect this.

i think we may be at cross-purposes : what i’m suggesting are methods which
operate over n Enumerable items in synchronicity - not instance methods to be
inherited by classes mixing in Enumerable… was this clear?

-a

···

On Wed, 27 Nov 2002 dblack@candle.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: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

_why:

It would be really cool if, instead of having a million
*_with_index methods floating around in Enumerable, we had a
method which could be used inside a block to retrieve the number
of iterations that the block has undergone.
[…]
This implementation is flawed, though, because it creates the
`index’ method in whatever namespace the block is originally
placed. But, you know… what if??
[…]
Why not have a global var (eg $# or something) that keeps the
current index whenever you’re iterating through an
array/string/whatever?

It would be consistent, extremely useful, less confusing and
completely straightforward.

OK… let the criticism flow!

One major problem: global.

Globals aren’t threadsafe, and by doing a global, I lose any context
capabilities for nested indexing.

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.26 at 23.42.45

···

On Wed, 27 Nov 2002 13:32:24 +0900, Jason Persampieri wrote:

Hi –

Why not have a global var (eg $# or something) that
keeps the current index whenever you’re iterating
through an array/string/whatever?

It would be consistent, extremely useful, less
confusing and completely straightforward.

OK… let the criticism flow!

(Aw, don’t put it that way :slight_smile:

Have a look at the “ToDo” file in the distribution – you’ll see:

  • discourage use of symbol variables (e.g. $/, etc.) in manual
  • discourage use of Perlish features by giving warnings.

So a new global of that kind probably isn’t on the horizon. I was
playing around with extending objects during iteration, so that you
could do:

ary.map {|e| puts “We’re at iteration #{e.iteration}”}

or something – but I haven’t come up with a way to do it that
fits more than a subset of cases. For one thing, you can’t
extend Fixnums, so anything based on that method won’t work if
your array contains those. Also, the syntax of block parameters
makes it hard: if you have

ary.map {|e,f,g| … }

I’m not sure there’s a way to determine which object got the
#iteration method added to it…

David

···

On Wed, 27 Nov 2002, Jason Persampieri wrote:


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

Hi –

[snip]

It could get unclean in a hurry, though :slight_smile:

class Thing < Array
def meth(other)
a = collect { |e| do_something_with(e) }
b = collect_with_index(self,other)… # ugh

end
end
[snip]

i’m unclear as to what you mean :

a = collect { |e| do_something_with(e) }

this would be Array#collect, what does it have to do with the following line?

b = collect_with_index(self,other)…

i don’t really understand to point of the example.

and why ‘ugh’? you have to have, at minimum, one method name and a name for
each Enumerable in the argument list : how could it get any
moresuccinct than that? if you have a better suggestion i can
incorporate it and post it…

What I don’t like is the idea that #collect and #collect_with_index
behave so differently: one being an iterator instance method, the
other a kind of utility/batch-iterator.

on second thought i would like to retract earlier statements about those
methods belonging in Kernel (what was i thinking ) and say those methods
should go in Enumerable as module methods so the above would need be

b = Enumerable.collect_with_index(self,other)…

i’ve updated

http://eli.fsl.noaa.gov/lib/ruby/site_ruby/1.6/enumerable.rb

to reflect this.

i think we may be at cross-purposes : what i’m suggesting are methods which
operate over n Enumerable items in synchronicity - not instance methods to be
inherited by classes mixing in Enumerable… was this clear?

We’re not at cross purposes – that is, I understood what you meant.
I just disagree :slight_smile: I think collect_with_index belongs among the
instance method of enumerables, just like collect, each, and
each_with_index.

David

···

On Wed, 27 Nov 2002, ahoward wrote:

On Wed, 27 Nov 2002 dblack@candle.superlink.net wrote:


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

Well, how about something like Regexp.last_match, where you can
make a call to retrieve the iteration data.

ary.map { |e,f,g|
ei = Enumerable.iteration_info
#…
}

···

On Wed, Nov 27, 2002 at 01:49:10PM +0900, dblack@candle.superlink.net wrote:

So a new global of that kind probably isn’t on the horizon. I was
playing around with extending objects during iteration, so that you
could do:

ary.map {|e| puts “We’re at iteration #{e.iteration}”}

or something – but I haven’t come up with a way to do it that
fits more than a subset of cases. For one thing, you can’t
extend Fixnums, so anything based on that method won’t work if
your array contains those. Also, the syntax of block parameters
makes it hard: if you have

ary.map {|e,f,g| … }

I’m not sure there’s a way to determine which object got the
#iteration method added to it…


Alan Chen
Digikata Computing
http://digikata.com

(Aw, don’t put it that way :slight_smile:

Have a look at the “ToDo” file in the distribution
– you’ll see:

  • discourage use of symbol variables (e.g. $/,
    etc.) in manual
  • discourage use of Perlish features by giving
    warnings.

So a new global of that kind probably isn’t on the
horizon. I was
playing around with extending objects during
iteration, so that you
could do:

ary.map {|e| puts “We’re at iteration
#{e.iteration}”}

or something – but I haven’t come up with a way to
do it that
fits more than a subset of cases. For one thing,
you can’t
extend Fixnums, so anything based on that method
won’t work if
your array contains those. Also, the syntax of
block parameters
makes it hard: if you have

ary.map {|e,f,g| … }

I’m not sure there’s a way to determine which object
got the
#iteration method added to it…

David


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

OK… globals = evil… more importantly cryptic code
= evil. I can appreciate that. And I think getting
rid of the extraneous _with_index methods would clean
things up a bit.

But I’m still determined to come up with a solution :wink:

Can we arbitrarily add a parameter to the block?
Something like:

ary.collect {|a,b,c,:iteration| …}

The idea is if there is an argument beginning with
‘:’, assign the iteration value/object for this
particular block to that.

That way, block are still easily nestable. As for
your other comment… is it really necessary to have
an index associated with each argument? Shouldn’t
they all be synchronized?

I’m not familiar yet with actual implementation
details, so I’m not sure how this would work… I’m
just throwing out UI ideas for now.

Jason

[snip]

What I don’t like is the idea that #collect and #collect_with_index
behave so differently: one being an iterator instance method, the
other a kind of utility/batch-iterator.
[snip]

they don’t behave differently at all, there are two of them : an instance
method #collect and the class method #collect !

Array#collect &block :
iterator instance method mixed in via Enumerable#collect

Array#each &block :
iterator instance method mixed in via Enumerable#each

Array#each_with_index &block :
iterator instance method mixed in via Enumerable#each_with_index

Enumerable#Enumerable.collect *enumerables, &block :
utility/batch-iterator module method

Enumerable#Enumerable.each *enumerables, &block :
utility/batch-iterator module method

Enumerable#Enumerable.each_with_index *enumerables, &block :
utility/batch-iterator module method

Enumerable#Enumerable.collect_with_index *enumerables, &block :
utility/batch-iterator module method

the instace method is the one we all know and love

the module method works on multiple instances of things which are Enumerable
and thus already have their own (instance) collect - your batch/utility
method. it just happens to have the same name since it’s the same thing!

it uses this idiom

class Foo

def method
  id
end

def Foo.method (*foos)
  foos.collect {|foo| foo.method}
end

end

which i personally like (and many will hate - i know)

eg. the class knows best how to apply a method to a bunch it’s instances
since perhaps there are efficiency steps which can be taken if the
implementation is known (like in this case).

the class method being named the same as the instance method make sense since
it does exactly the same thing.

it makes sense for the utility/batch method to be in the class since that’s
where the instance method is also defined - imho.

where would you put them?

what would you call them?

i would object to new names (like zip) on the basis that every one already
knows what collect, each_with_index, etc. do - and it’s already doccumented
so the class method would be easy to grasp even without additional docs. most
ruby novices have a bit of a time grasping the iterator methods - why throw a
bunch of new names at them (or me :wink: )?

[snip]

We’re not at cross purposes – that is, I understood what you meant.
I just disagree :slight_smile: I think collect_with_index belongs among the
instance method of enumerables, just like collect, each, and
each_with_index.
[snip]

yes, i agree. my last post has them there and refers to this? check out the
source and let me know what you think.

-a

···

On Wed, 27 Nov 2002 dblack@candle.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: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

This looks very promising, but doesn’t allow you to determine which iterator
we’re talking about in the case of nested iterators.

Tim Bates

···

On Wed, 27 Nov 2002 04:21 pm, Alan Chen wrote:

Well, how about something like Regexp.last_match, where you can
make a call to retrieve the iteration data.

ary.map { |e,f,g|
ei = Enumerable.iteration_info
#…
}


tim@bates.id.au