This was the tip of the week in the January 6, 2022 Ruby Weekly Newsletter.


If you’re an avid Rails user, you might be familiar with ActiveSupport’s Hash#except which has been around for roughly 15 years. As of Ruby 3.0, Hash#except is now a native Ruby method too!

But… what does it do? Hash#except gives us a hash excluding all of the keys we’ve passed as arguments. Let’s look at an example:

jemma = { name: "Jemma", username: "jemma", password: "super secure" }

jemma.except(:password)
# => { name: "Jemma", username: "jemma" }

Before Ruby 3.0, if we were in a repo which wasn’t using Rails, we could have done something like this:

jemma = { name: "Jemma", username: "jemma", password: "super secure" }

jemma.reject { |k,_| k == :password }
# => { name: "Jemma", username: "jemma" }

While this works totally fine, it gets a little clunky if we’re trying to exclude multiple different keys. Yet Hash#except can also take multiple arguments:

jemma = { name: "Jemma", username: "jemma", password: "super secure" }

jemma.except(:name, :password)
# =>  { username: "jemma" }

Hash#except might look familiar because it’s the inverse of Hash#slice. Hash#slice will give us a hash containing key / value pairs whose keys we specify as arguments, while Hash#except will give us a hash containing key / value pairs whose keys we don’t specify as arguments.

We might be wondering, what happens if we pass a key that is not in the hash?

jemma = { name: "Jemma", username: "jemma", password: "super secure" }

jemma.except("key doesn't exist")
# => { name: "Jemma", username: "jemma", password: "super secure" }

In this case, Ruby is technically still excluding this key from our hash - it just never appeared in the hash to begin with. (This is the same behavior as Hash#slice.)