Hashes and Enumerable#each_with_index

OK, looking at the archives I know this was discussed a few years ago,
but there seems to be no real conclusion or improvement since then.

The problem: Hash#each_with_index returns an index that is not usable
to address the hash. That is, given the following:

h = Hash.new
h.each_with_index {
    > o, i |
    ...
}

i is not defined such that h[i] == o, which is both misleading and
seemingly useless. (This behavior, as per the previous discussion, can
be achieved with h.to_a.each_with_index.)

Would it not be more useful to adopt the definiton of
Enumerable#each_with_index where the index given is such that e[i] == o
for any given Enumerable?

A specific example of where this is useful is in my current codebase,
where I add _pre_store to Enumerable, for Marshalling objects. This
walks through (using each_with_index) the Enumerable, replacing indices
that refer to persistant objects with temporary references, to be
restored on load.

Given the current definition, which returns only numeric indices, this
makes any Enumerable subclass that does not use numeric indices not
follow this pattern, thus requiring specific coding. This seems like a
waste. Enumerable provides no other interface for obtaining addressable
indices.

Since containers like this all mixin Enumerable, it would seem that
using this definition would be much more accurate and useful… or at
least some interface in Enumerable that acted this way, such as
each_index.

···


Ryan Pavlik rpav@users.sf.net

“Now is not the time for sarcasm! Now is the time
for nefarious action.” - 8BT

Hi –

OK, looking at the archives I know this was discussed a few years ago,
but there seems to be no real conclusion or improvement since then.

The problem: Hash#each_with_index returns an index that is not usable
to address the hash. That is, given the following:

h = Hash.new
h.each_with_index {
    > o, i |
    ...
}

i is not defined such that h[i] == o, which is both misleading and
seemingly useless. (This behavior, as per the previous discussion, can
be achieved with h.to_a.each_with_index.)

Would it not be more useful to adopt the definiton of
Enumerable#each_with_index where the index given is such that e[i] == o
for any given Enumerable?

One problem I see is: how could you implement this, in Enumerable, in
terms of #each?

The current #each_with_index is essentially:

i = 0
each {|e| yield(e,i); i += 1}

If the new method (which I think should have a new name :slight_smile: did the
above for arrays, but did this for hashes:

each {|k,v| yield(v,k)}

you’d be thrown back on defining something differently for different
classes that mixed in Enumerable.

David

···

On Fri, 25 Apr 2003, Ryan Pavlik wrote:


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

Hi,

···

In message “Hashes and Enumerable#each_with_index” on 03/04/25, Ryan Pavlik rpav@nwlink.com writes:

The problem: Hash#each_with_index returns an index that is not usable
to address the hash. That is, given the following:

h = Hash.new
h.each_with_index {
> o, i |

}

i is not defined such that h[i] == o, which is both misleading and
seemingly useless. (This behavior, as per the previous discussion, can
be achieved with h.to_a.each_with_index.)

Would it not be more useful to adopt the definiton of
Enumerable#each_with_index where the index given is such that e[i] == o
for any given Enumerable?

No. Index in each_with_index means offset of the values. It might be
possible to implement a method that you described for arrays, hashes,
string, etc. But it should have different name.

						matz.

Ryan Pavlik wrote:

OK, looking at the archives I know this was discussed a few years ago,
but there seems to be no real conclusion or improvement since then.

The problem: Hash#each_with_index returns an index that is not usable
to address the hash. That is, given the following:

h = Hash.new
h.each_with_index {
    > o, i |
    ...
}

i is not defined such that h[i] == o, which is both misleading and
seemingly useless. (This behavior, as per the previous discussion, can
be achieved with h.to_a.each_with_index.)

Would it not be more useful to adopt the definiton of
Enumerable#each_with_index where the index given is such that e[i] == o
for any given Enumerable?

This operation seems most sensical, to me, when used with ordered
hashes. Here’s a patch for Gotoken’s “pseudohash” package on the RAA:

add to pseudohash.rb (not heavily tested)

def
if k.kind_of?(Fixnum)
key = order[k]
return self.fetch(key)
else
return super
end
end

def each_with_index
rest = keys - order
order.each{|k,v| yield(k,self[k],order.index(k)) if include?(k)}
rest.each{|k,v| yield(k,self[k],order.index(k)) }
end

example:

record = PseudoHash.new.with_order [‘Alice’, ‘Bob’, ‘Charlee’]
record[‘Charlee’] = 90
record[‘Alice’] = 91
record[‘Bob’] = 89
record.each_with_index{|k,v,i|
puts “Key: #{k}”
puts “Index: #{i}”
puts "Using index: " + record[i].to_s
puts "Using key: " + record[k].to_s
puts “=” * 10
}

Regards,

Dan

a = [74, 117, 115, 116, 32, 65, 110, 111, 116, 104, 101, 114, 32, 82]
a.push(117,98, 121, 32, 72, 97, 99, 107, 101, 114)
puts a.pack(“C*”)

Or indeed just ‘h.each’ (if you replace |o,i| with |i,o|)

It did seem a bit strange to me at first that the methods don’t really line
up:

Array#each              <=>    Hash#each_value
Array#each_with_index   <=>    Hash#each   (but with args reversed)

Regards,

Brian.

···

On Fri, Apr 25, 2003 at 07:26:01AM +0900, Ryan Pavlik wrote:

OK, looking at the archives I know this was discussed a few years ago,
but there seems to be no real conclusion or improvement since then.

The problem: Hash#each_with_index returns an index that is not usable
to address the hash. That is, given the following:

h = Hash.new
h.each_with_index {
    > o, i |
    ...
}

i is not defined such that h[i] == o, which is both misleading and
seemingly useless. (This behavior, as per the previous discussion, can
be achieved with h.to_a.each_with_index.)

Quoteing rpav@nwlink.com, on Fri, Apr 25, 2003 at 07:26:01AM +0900:

OK, looking at the archives I know this was discussed a few years ago,
but there seems to be no real conclusion or improvement since then.

The problem: Hash#each_with_index returns an index that is not usable
to address the hash. That is, given the following:

h = Hash.new
h.each_with_index {
    > o, i |
    ...
}

i is not defined such that h[i] == o, which is both misleading and
seemingly useless. (This behavior, as per the previous discussion, can
be achieved with h.to_a.each_with_index.)

This function already exists, for a Hash its called Hash#each.

I guess Hash could override the each_with_index it gets from enumerable,
aliasing it to each, but that seems like it would be LESS useful, not
more useful.

Sam

Would it not be more useful to adopt the definiton of
Enumerable#each_with_index where the index given is such that e[i] == o
for any given Enumerable?

Hmm, not a good idea in general, I think.
But you could always make ‘each_with_key’
which would work that way, while leaving
each_with_index as Matz designed (and
likes) it, and as we are used to it.

Hal

Hi –

> > Would it not be more useful to adopt the definiton of > > Enumerable#each_with_index where the index given is such that e[i] == o > > for any given Enumerable? > > One problem I see is: how could you implement this, in Enumerable, in > terms of #each? >

I don’t see why there must exist a default implementation in
Enumerable… this is a good example of why. I personally have no
preference as to whether it has a new name or not, but I’m guessing
there’s little choice since likely a good deal of code already exists
that relies on the current behavior.

···

On Fri, 25 Apr 2003 07:40:47 +0900 dblack@superlink.net wrote:

On Fri, 25 Apr 2003, Ryan Pavlik wrote:


Ryan Pavlik rpav@users.sf.net

“Now is not the time for sarcasm! Now is the time
for nefarious action.” - 8BT

Hi,

> >Would it not be more useful to adopt the definiton of > >Enumerable#each_with_index where the index given is such that e[i] == o > >for any given Enumerable? > > No. Index in each_with_index means offset of the values. It might be > possible to implement a method that you described for arrays, hashes, > string, etc. But it should have different name.

I actually don’t really care here… new name (the each_with_key
suggestion sounds OK to me) or old name, as long as it’s there, it’d be
a little more useful, IMHO.

···

On Fri, 25 Apr 2003 08:10:04 +0900 matz@ruby-lang.org (Yukihiro Matsumoto) wrote:

In message “Hashes and Enumerable#each_with_index” > on 03/04/25, Ryan Pavlik rpav@nwlink.com writes:


Ryan Pavlik rpav@users.sf.net

“Now is not the time for sarcasm! Now is the time
for nefarious action.” - 8BT

Hi,

No. Index in each_with_index means offset of the values. It might be
possible to implement a method that you described for arrays, hashes,
string, etc. But it should have different name.

I actually don’t really care here… new name (the each_with_key
suggestion sounds OK to me) or old name, as long as it’s there, it’d be
a little more useful, IMHO.

I’m positive. But describe what you want more precisely. The
following behavior OK for you?

Suppose it is called “each_with_foo”:

* each_with_foo should be available for all built-in classes that
  have obj[x] access.

* each_with_foo must give (x,y) that obj[y] == x be true always.

Replace “foo” with the right word. I don’t think it’s “key”, since
Array index is not key after all.

						matz.
···

In message “Re: Hashes and Enumerable#each_with_index” on 03/04/25, Ryan Pavlik rpav@nwlink.com writes:

Hi –

···

On Fri, 25 Apr 2003, Ryan Pavlik wrote:

On Fri, 25 Apr 2003 07:40:47 +0900 > dblack@superlink.net wrote:

Hi –

On Fri, 25 Apr 2003, Ryan Pavlik wrote:

> > Would it not be more useful to adopt the definiton of > > Enumerable#each_with_index where the index given is such that e[i] == o > > for any given Enumerable? > > One problem I see is: how could you implement this, in Enumerable, in > terms of #each? >

I don’t see why there must exist a default implementation in
Enumerable… this is a good example of why. I personally have no
preference as to whether it has a new name or not, but I’m guessing
there’s little choice since likely a good deal of code already exists
that relies on the current behavior.

If it applies to “any given Enumerable”, then presumably it’s defined
in Enumerable :slight_smile:

David


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

> I'm positive. But describe what you want more precisely. The > following behavior OK for you? > > Suppose it is called "each_with_foo": > > * each_with_foo should be available for all built-in classes that > have obj[x] access. > > * each_with_foo must give (x,y) that obj[y] == x be true always. > > Replace "foo" with the right word. I don't think it's "key", since > Array index is not key after all.

David Black (iirc) had a good suggestion on #ruby-lang, and that was
"each_pair".

···

On Fri, 25 Apr 2003 15:25:14 +0900 matz@ruby-lang.org (Yukihiro Matsumoto) wrote:


Ryan Pavlik rpav@users.sf.net

“Now is not the time for sarcasm! Now is the time
for nefarious action.” - 8BT

Yukihiro Matsumoto wrote:

* each_with_foo should be available for all built-in classes that
  have obj[x] access.

* each_with_foo must give (x,y) that obj[y] == x be true always.

Replace “foo” with the right word. I don’t think it’s “key”, since
Array index is not key after all.

Or, have each class that wants to support this protocol define a method
‘each_foo’. Then have enumerable implement ‘each_with_foo’ as

module Enumerable
def each_with_foo
each_foo {|foo| yield self[foo], foo}
end
end

Then the change to Hash is simply

alias each_foo each_key

and to array

alias each_foo each_index

However, how often to you want to do an each_with_index polymorphically?

Cheers

Dave

Hi –

···

On Fri, 25 Apr 2003, Ryan Pavlik wrote:

On Fri, 25 Apr 2003 15:25:14 +0900 > matz@ruby-lang.org (Yukihiro Matsumoto) wrote:

> I'm positive. But describe what you want more precisely. The > following behavior OK for you? > > Suppose it is called "each_with_foo": > > * each_with_foo should be available for all built-in classes that > have obj[x] access. > > * each_with_foo must give (x,y) that obj[y] == x be true always. > > Replace "foo" with the right word. I don't think it's "key", since > Array index is not key after all.

David Black (iirc) had a good suggestion on #ruby-lang, and that was
“each_pair”.

It was flgr – not sure who that is though.

David


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

> Or, have each class that wants to support this protocol define a method > 'each_foo'. Then have enumerable implement 'each_with_foo' as > > module Enumerable > def each_with_foo > each_foo {|foo| yield self[foo], foo} > end > end

This is perhaps an OK default, but it would be nice to see a O(1)
iterator for Hash eventually. :wink:

> However, how often to you want to do an each_with_index polymorphically?

This is the point of the addition—I want to access every Enumerable
from Enumerable, to be able to replace and restore references to
Marshalled objects (so other objects stored with an OID don’t get
cloned by the store/load). It’s better to do this once in Enumerable
than for every class individually.

There are, I’m sure, a myriad of uses for e[k] == i, though. Not having
to look up the method for every class ought to be one. :wink:

···

On Fri, 25 Apr 2003 15:55:06 +0900 Dave Thomas dave@pragprog.com wrote:


Ryan Pavlik rpav@users.sf.net

“These monkeys of which you speak… are they mentally
deficient by human or monkey standards?” - 8BT