Array#join using to_a if defined

Hi,

I got recently an unexpected result using Array#join.

In my thoughts, Array#join is sth like adding the separator between
every
element#to_s.

That is what seems to happen when there is no any #to_a method defined
in an
element:

For example,
class N < Struct.new(:n)
def to_s
‘to_s’
end
end

[N.new(2), N.new(3)].join(’ ') #=> “2 3” and not “to_s to_s”

This is due to Struct::new that defines #to_a that return an Array of
instance variables (here :n).
That’s quite annoying, because you would expect when you’re defining
to_s to
be the only usual String representation.
The only way to edit that behavior without changing Array#join, is to
“undef_method :to_a”, not very nice …
or to use ary.map(&:to_s).join, very redundant.

Shouldn’t Array#join looks like:
class Array
def join(sep = $,)
sep = ‘’ if sep.nil?
s = “”
self.each_with_index { |e, i|
s << e.to_s
s << sep unless i == size-1
}
s
end
end

Why is it this ‘look’ to element#to_a ? is that really interesting?
when?
Maybe when you use [[1],[2,3]].join #=> “123” ? Then we should look for
#to_ary, not #to_a

Regards,
B.D.

On Sun, Feb 28, 2010 at 12:53 PM, Benoit D. [email protected]
wrote:

For example,
class N < Struct.new(:n)
def to_s
‘to_s’
end
end

[N.new(2), N.new(3)].join(’ ') #=> “2 3” and not “to_s to_s”

I got “to_s to_s” in 1.8.6, 1.8.7, and 1.9.1

On 1 March 2010 04:25, Josh C. [email protected] wrote:

That is what seems to happen when there is no any #to_a method defined in
[N.new(2), N.new(3)].join(’ ') #=> “2 3” and not “to_s to_s”

I got “to_s to_s” in 1.8.6, 1.8.7, and 1.9.1

Me:
irb for ruby-1.9.2-r26764

class N < Struct.new(:n)
def to_s
‘to_s’
end
end
=> nil
[N.new(2), N.new(3)].join(’ ')
=> “2 3”

irb for ruby-1.8.7-p249

class N < Struct.new(:n)
def to_s
‘to_s’
end
end
=> nil
[N.new(2), N.new(3)].join(’ ')
=> “to_s to_s”

So this would be only 1.9.2 behavior, but why on world this changed? It
has
such bad side-effect in this case.


Also, while playing with Array, using Array#&, I saw it look on #eql?
and
#hash methods. These two methods are relative to Hash usually, I didn’t
expect methods of Array to depends of them.
A surprising result to me:

irb for ruby-1.9.2-r26764

[1,2]&[2]
=> [2]
class N
def initialize(n)
@n = n
end
attr_reader :n
def == o
@n == o.n
end
end
=> nil
[N.new(1),N.new(2)]&[N.new(2)]
=> [] # instead of [#<N:… @n=2>]

On 3/1/10, Benoit D. [email protected] wrote:

Also, while playing with Array, using Array#&, I saw it look on #eql? and
#hash methods. These two methods are relative to Hash usually, I didn’t
expect methods of Array to depends of them.

It’s because the efficient (O(1)) implementation of Array#& requires
creating a temporary hash (or 2?) internally. However, this would seem
to be an internal detail and callers should not have to know about it,
as your example (quoted below) demonstrates.

2010/3/1 Benoit D. [email protected]:

So this would be only 1.9.2 behavior, but why on world this changed? It has
such bad side-effect in this case.

Maybe #flatten’s functionality was extended to also include objects
that implement to_a because to_a has been removed from Object in
1.9…

Also, while playing with Array, using Array#&, I saw it look on #eql? and
#hash methods. These two methods are relative to Hash usually, I didn’t
expect methods of Array to depends of them.

Learn something new every day. :wink:

Kind regards

robert