rubyguides

Hash#slice

Hash#slice(*keys) → a_hash

What it does

Hash#slice gives you a way to grab just the key-value pairs you need from a hash. You tell it which keys you want, and it hands back a new hash containing only those pairs. Your original hash stays exactly as it was — slice does not change it.

Think of it like a buffet. You have a plate full of different foods, but you only want a few of them. You pick out what you want and put it on your own plate. The original plate (your original hash) still has everything on it.

Signature

# Syntax (not runnable code):
Hash#slice(*keys) → a_hash

The method accepts any number of key arguments using the splat operator (*). It returns a new Hash object.

Parameters

  • *keys — One or more keys to extract from the hash. You can pass as many keys as you need, separated by commas.

Return Value

The method returns a new Hash containing only the key-value pairs for keys that exist in the original hash. If none of the requested keys exist, you get an empty hash back. The original hash is never modified.

Basic Usage

With a single key:

config = { host: "localhost", port: 3000, debug: true }
config.slice(:host)  # => {:host=>"localhost"}
config               # => {:host=>"localhost", :port=>3000, :debug=>true}

The single-key example above shows the simplest case: pick one key and get a one-entry hash back. But slice really shines when you need several keys at once, because it handles them all in a single call rather than chaining multiple lookups. The method always returns a new hash, so the original config remains untouched after the call — a property that makes it safe to use inside methods that shouldn’t have side effects on their inputs.

The original config still contains all three keys after calling slice — the method is non-destructive.

You can request multiple keys at once:

data = { a: 100, b: 200, c: 300 }
data.slice(:a, :c)  # => {:a=>100, :c=>300}

When you pass several keys, slice returns them in the order you requested, not the order they appear in the original hash. This ordering guarantee is useful when you need predictable output, such as building a serialized response where field order matters. Keys that don’t exist in the source hash are simply omitted from the result without raising an error.

Handling missing keys

If you ask for a key that does not exist, slice simply ignores it. No error is raised.

user = { name: "Alice", email: "alice@example.com" }
user.slice(:name, :phone, :address)
# => {:name=>"Alice"}

The :phone and :address keys do not exist in the hash, so they are silently skipped. You only get back the pairs that actually exist.

Result Ordering

The order of the key-value pairs in the result matches the order in which you listed the keys in your argument list, not the order they appear in the original hash.

{ c: 3, a: 1, b: 2 }.slice(:b, :a, :c)
# => {:b=>2, :a=>1, :c=>3}

This ordering behavior is distinct from most other Hash methods, which preserve insertion order from the original hash. Use it when the consumer of the returned hash expects fields in a specific sequence — for instance, when generating CSV headers or building a JSON response where the key order communicates meaning to a frontend component.

This can be useful when you need a predictable output order.

Common use cases

Parameter Whitelisting

A very common use case is restricting user input to only the keys your method accepts. This prevents unexpected or malicious keys from entering your system.

# Only allow these specific parameters
def create_user(params)
  permitted = params.slice(:name, :email, :password)
  # Now permitted contains only the keys you expect
end

user_input = { name: "Bob", email: "bob@test.com", role: "admin", token: "***" }
create_user(user_input)
# => {:name=>"Bob", :email=>"bob@test.com"}

Even though the input hash contained :role and :token keys, the slice call filters them out completely. This is why slice is the preferred approach for parameter whitelisting in plain Ruby — it’s declarative, non-destructive, and reads like a whitelist of what you intend to accept. The returned hash is guaranteed to contain only the keys you listed, with no extras slipping through.

Only the keys that exist in the original hash and are explicitly requested make it through. Keys that do not exist are silently ignored.

Extracting configuration

When working with configuration hashes, you often want to pass only a subset of settings to another component:

app_config = { host: "0.0.0.0", port: 8080, workers: 4, log_level: "debug" }
server_config = app_config.slice(:host, :port)
# => {:host=>"0.0.0.0", :port=>8080}

The configuration-extraction pattern above works well when your app settings hash uses symbol keys. But not all hashes use symbols — many libraries and JSON parsers produce hashes with string keys instead. The distinction matters because slice compares keys by exact identity, not by equivalence.

Symbol keys vs string keys

Ruby distinguishes between symbol keys and string keys. {a: 1} and {"a" => 1} are completely different hashes. Calling slice with symbol keys on a string-keyed hash (or vice versa) returns an empty hash:

{"host" => "localhost"}.slice(:host)
# => {}

An empty result like the one above is easy to miss in code review — no exception is raised, and the program continues silently with a hash that has none of the expected entries. When integrating with external services or libraries that use string-keyed hashes, either convert the keys with transform_keys(&:to_sym) or use string arguments in your slice calls. The mismatch between symbol and string keys is one of the most common sources of subtle bugs in Ruby hash manipulation.

This matters when working with APIs or libraries that use one style exclusively.

Ruby 3 keyword arguments

In Ruby 3, hashes and keyword arguments are handled separately. If a method expects keyword arguments but receives a hash, slice alone will not convert between them:

def greet(name:, age:)
  puts "Hello, #{name}!"
end

config = { name: "Alice", age: 30 }
# greet(config.slice(:name, :age))  # => TypeError: wrong arguments

When a method signature specifies keyword arguments, Ruby 3 requires the caller to use the ** operator to unpack a hash into those arguments. Passing a plain hash — even one from slice — raises a TypeError because Ruby treats the whole hash as a single positional argument rather than as separate keyword arguments.

To pass a sliced hash as keyword arguments, use the double-splat operator:

greet(**config.slice(:name, :age))  # => "Hello, Alice!"

The double-splat operator (**) converts a hash into keyword arguments, which is exactly what you need when bridging between a hash-based configuration and a method that expects explicit keyword parameters. In Ruby 3 and later, this is the canonical way to pass a subset of hash keys as keyword arguments without triggering an argument error or a hash-to-keyword deprecation warning.

Edge Cases

No keys given

Calling slice with no arguments returns an empty hash:

{a: 1, b: 2}.slice()  # => {}

Calling slice with no arguments always produces an empty hash, regardless of the source hash’s content. This is consistent with the method’s semantics — you asked for zero keys, so you get zero entries back. The same principle applies to the edge cases below.

Empty hash

If you call slice on an empty hash, you always get an empty hash back regardless of what keys you ask for:

{}.slice(:a, :b)  # => {}

Both the empty-hash case and the all-keys-missing case produce the same result — an empty hash — but for different reasons. When the source hash is empty, there’s nothing to extract. When all requested keys are absent, slice silently skips them. Neither scenario raises an exception, which means your code must explicitly check for an empty result when the presence of certain keys is required for correctness.

All keys missing

When none of the requested keys exist in the hash, you get an empty hash:

{ x: 1, y: 2 }.slice(:a, :b)  # => {}

See Also