Forum: Ruby Array#=== as set equality

Posted by Thomas Sawyer (7rans)
on 2010-09-01 11:04
(Received via mailing list)
It looks like Array#== and Array#=== are the same. Is that correct?
That being so, would there be any serious consequences if Array#===
were redefined to mean "set equality", e.g.

  [:a,:b] === [:b,:a]    #=> true

I can think of a few times that would have been nice to have,
especially when testing and hashes come into play, the results often
do not have a fixed order. e.g.

  h = {:a=>1, :b=>2}
  r = h.map{ |k,v| [k,v] }

  assert  r == [[:a,1], [:b,2]]

won't work b/c it might also return

  [[:b,2], [:a,1]]
Posted by Robert Dober (Guest)
on 2010-09-01 15:53
(Received via mailing list)
On Wed, Sep 1, 2010 at 11:03 AM, Intransition <transfire@gmail.com> 
wrote:
> It looks like Array#== and Array#=== are the same. Is that correct?
> That being so, would there be any serious consequences if Array#===
> were redefined to mean "set equality", e.g.
I'd prefer
>  assert  r == [[:a,1], [:b,2]]
yup but in your case could you not do something like

assert_equal h, Hash[*r.flatten]

I say that because I'd prefer #=== to have the semantics of include?,
so that I can do

case some_value
when some_array
when other_array
end

Cheers
Robert
Posted by Christopher Dicely (Guest)
on 2010-09-01 17:19
(Received via mailing list)
On Wed, Sep 1, 2010 at 2:03 AM, Intransition <transfire@gmail.com> 
wrote:
> It looks like Array#== and Array#=== are the same. Is that correct?
> That being so, would there be any serious consequences if Array#===
> were redefined to mean "set equality", e.g.
>
>  [:a,:b] === [:b,:a]    #=> true

Well, it breaks existing code that relies on the existing behavior in
case statements (don't know how common that is), and if it was going
to be redefined, I think it would be more natural to have it reflect
inclusion (like Range#===) so that, e.g.,

[:a,:b] === :a #=> true

OTOH, if you need something that is initialized by a value of a
particular type but provides special match semantics, its pretty
trivial to do; given the number of different things people might want
#=== to do on a particular class, and existing code that depends on
the existing behavior, its usually probably best not to change #===
for existing (especially core) classes, but just to define matcher
classes as needed. E.g.,

class OrderInsensitiveArrayMatcher
  def initialize(array)
    @array = array.dup
  end
  def ===(other)
    identity = ->(x) {x}
    @array.group_by(&identity) == other.group_by(&identity)
  rescue
    nil
  end
end

require 'set'

class SetMatcher
  def initialize(enum)
    @set = Set.new(enum)
  end
  def ===(other)
    @set == other.to_set
  rescue
     nil
  end
end

etc.

If there are common use cases, libraries of useful matchers could be
built so that everyone isn't reinventing the wheel, but without
breaking existing code.
Posted by F. Senault (Guest)
on 2010-09-01 17:35
(Received via mailing list)
Le 01 septembre à 11:03, Intransition a écrit :

>   assert  r == [[:a,1], [:b,2]]

Well, you're talking about set semantics.  Why not :

>> r.to_set == [[:b, 2], [:a, 1]].to_set
=> true

?

You could define things like assert_as_set which maps the arguments with
to_set for you, for instance.

(Note that I'm not a regular user of the unit testing frameworks.)

Fred
Posted by Joel VanderWerf (Guest)
on 2010-09-01 17:54
(Received via mailing list)
On 09/01/2010 06:49 AM, Robert Dober wrote:
> I say that because I'd prefer #=== to have the semantics of include?,
> so that I can do
>
> case some_value
> when some_array
> when other_array
> end

Is an asterisk too much typing?

a = [1,2]
b = [3,4]

case 3
when *a; puts "A"
when *b; puts "B"
end
Posted by Benoit Daloze (Guest)
on 2010-09-01 18:01
(Received via mailing list)
On 1 September 2010 17:53, Joel VanderWerf <joelvanderwerf@gmail.com> 
wrote:
> Is an asterisk too much typing?
>
> a = [1,2]
> b = [3,4]
>
> case 3
> when *a; puts "A"
> when *b; puts "B"
> end
>

That is rather cool, I did not know splat was working better than
expected here too.

Thanks,
B.D.
Posted by Joel VanderWerf (Guest)
on 2010-09-01 18:23
(Received via mailing list)
On 09/01/2010 09:01 AM, Benoit Daloze wrote:
>>
>
> That is rather cool, I did not know splat was working better than
> expected here too.
>
> Thanks,
> B.D.
>

Btw, it works in rescue as well.

ALL_NETWORK_ERRORS = [Errno::ECONNRESET, Errno::ECONNABORTED,
     Errno::ECONNREFUSED, Errno::EPIPE, IOError, Errno::ETIMEDOUT]

require 'socket'
begin
   TCPSocket.new("localhost", 9999)
rescue *ALL_NETWORK_ERRORS => ex
   p ex
end
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.