Hash Comparison in Ruby 2.3

来源:转载


It's been over a year since I wrote my initial "Proposal for a better Ruby Hash#include?"and I'm so happy to announce that since Tuesday November 10, 2015, Hash comparison methods have been committed to MRI's trunk branchby the fabulous Nobuyoshi Nakada.

This means the following Hash comparison methods will be available in Ruby 2.3 when it's released this coming Christmas. In the meantime you can install Ruby 2.3.0-preview1which was released today and already includes this new feature.

Comparing Hashes

What is hash comparison? This may be hard to visualize, so here's a short example:

hash_one = { a: 1 }hash_two = { a: 1, b: 2 }

It's clear just by looking at hash_twothat it includes the same single key and value as hash_oneplus one extra key and value. So how do you check that with Ruby? Intuitively you may try the Hash#include? method, right? Let's see.

# Ruby MRI 2.2 and earlier{ a: 1, b: 2 }.include?({ a: 1 })=> false

Hmmm. Strange, no? Well that's because Hash#include?doesn't compare hashes, it only looks at the keys. Worse, it will only work properly with keys as arguments. So in Ruby 2.2 and earlier, the only thing you can do is check that a hash includes the same key, like this:

# Ruby MRI 2.2 and earlier{ a: 1, b: 2 }.include?(:a)=> true

This is because Hash#include?is in fact a method alias for Hash#has_key? .

This is surprising behavior to say the least, and some Ruby libraries like RSpec implement their own Hash inclusion logicto circumvent the strange behavior of Hash#include?. As you'd expect, RSpec's includematcher checks hashes for both key andvalue matching. So you can write:

# RSpecexpect({ a: 1, b: 2 }).to include({ a: 1 }) Hash Comparison Methods

My original feature proposal suggested introducing an new Hash#contain?method, but the semantics didn't work well enough for the Ruby core developers. Matz himself said the following in the November developer meeting:

Hash#contain? has [a] slight ambiguity problem. I'd vote for adding >=, along with <=.

At first I was a little taken aback by Matz's proposal, but Erik Michaels-Oberpointed out that I should be happy my little method proposal was becoming an operator on Hashes. And now I see, Matz and Erik were right, look how great this is:

# Ruby MRI 2.3.0 (trunk){ a: 1, b: 2 } >= { a: 1 }=> true

For the less mathy people (like me), this may take a second to wrap your head around. I find it easier to say the operator out loud:

"Is this hash greater than or equal to this other hash?".

A hash that contains another smaller hash is greater, so this returns true. A hash that contains the same hash is equal, so this returns true.

Here are a few more examples provided by Akira Tanaka:

# Ruby MRI 2.3.0 (trunk){ a: 1 } <= { a: 1 } = true{ a: 1 } <= { a: 2 } = false{ a: 2 } <= { a: 1 } = false{ a: 2 } <= { a: 2 } = true{ a: 1 } >= { a: 1 } = true{ a: 1 } >= { a: 2 } = false{ a: 2 } >= { a: 1 } = false{ a: 2 } >= { a: 2 } = true{ a: 1 } < { a: 1 } = false{ a: 1 } < { a: 2 } = false{ a: 2 } < { a: 1 } = false{ a: 2 } < { a: 2 } = false{ a: 1 } > { a: 1 } = false{ a: 1 } > { a: 2 } = false{ a: 2 } > { a: 1 } = false{ a: 2 } > { a: 2 } = false{ a: 1 } == { a: 1 } = true{ a: 1 } == { a: 2 } = false

Look at how consistent this is. You can now compare hashes exactly the same way you would compare integers.

I can't wait for Ruby 2.3.



分享给朋友:
您可能感兴趣的文章:
随机阅读: