# Pattern Matching In Ruby

> Understand pattern matching in Ruby.

- Canonical: https://dennisokeeffe.com/blog/2022-02-15-pattern-matching-in-ruby
- Published: 2022-02-15
- Updated: 2022-02-15
- Tags: ruby

Pattern matching in Ruby enables the capability to check data conformity with a particular pattern and to handle appropriately based on the match (or a default action/error).

Introduced in Ruby 2.7, most of the pattern matching features are no longer considered experimental as of Ruby 3.1 (although there are some differences with pattern matching in Ruby 3 that will be explored).

## Hello, pattern matching!

The pattern matching block opens with the keyword `case` and the expression to be checked is passed in as the first argument. This is the same as the `case` statements you will be familiar with.

We can check against cases with the `in/then` keyword combination e.g. `in 'VALUE' then do_something`. The difference between the `case` statement we are familiar with and pattern matching is that `in` is used instead of `when`. A default can be set with the `else` keyword, where we can execute the default behavior, otherwise `NoMatchingPatternError` will be raised.

Below is a simple example:

```rb
team = 'A'

case team
in 'A' then puts 'Team A'
in 'B' then puts 'Team B'
else puts 'Team C'
end

# => "Team A"
```

If we omit the `else` and match against an unhandled case, we note that the `NoMatchingPatternError` is raised.

```rb
begin
	team = 'D'
	case team
	in 'A' then puts 'Team A'
	in 'B' then puts 'Team B'
	end

	puts 'Still executes'
rescue NoMatchingPatternError
	puts 'No matching pattern'
end

# => "No matching pattern"
```

## Matching patterns

There are a number of ways that we can match against a pattern. Below are some examples:

- **Object pattern** match (comparing two objects).
- **Variable pattern** which binds a variable(s) to the values that match the pattern.
- **As pattern** which manages more complex variable assignments.
- **Alternative pattern** match which allows you to match against multiple patterns in one case i.e. `in 0 | 1 | 2`.
- **Guard conditions** which can be used to execute a pattern match if a condition is met i.e. `in 'A' if condition_is_true`.
- **Array pattern** match which allows us to match against an array of values based on value or object match (including matches with a splat `*` or "any value" `_`).
- **Hash pattern** matching (which only works for symbol keys and not string keys) which can also work with matching on a double splat `**`. Be mindful that the hash pattern will match cases with only a subset of keys matching.
- **Find pattern** which will help match against part of an array.

Some useful examples of some of the above.

The **Object pattern**:

```rb
# Object pattern
class Input
end

value = Input.new

case value
in Input then puts 'Object is Input'
end

# => Object is Input
```

The **Alternative pattern**:

```rb
value = 0

case value
in 0 | 1 | 2 then puts 'Value is 0, 1 or 2'
end

# => Value is 0, 1 or 2
```

**Guard conditions**:

```rb
is_allowed = false
value = 0

case value
in 0 if is_allowed then puts 'Value is 0'
end

# => NoMatchingPattern error raised
```

**Hash pattern**:

```rb
a = {a: 1, b: 2, c: 3}
b = {a: 2, b: 3, c: 4}

c = a

case c
in a then puts 'Hash matches "a"'
in b then puts 'Hash matches "b"'
end

# => Hash matches "a"

case c
in {a:, **rest} then puts 'Hash matches splat'
in a then puts 'Hash matches "a"'
in b then puts 'Hash matches "b"'
end

# => Hash matches splat
```

## Summary

Today's post is a short overview on pattern matching, how it differs from case statements and how it can be used to solve a variety of matching problems.

## Resources and further reading

- [Ruby docs: Pattern Matching](https://docs.ruby-lang.org/en/3.0/doc/syntax/pattern_matching_rdoc.html)
- [The Odin Project: Pattern Matching](https://www.theodinproject.com/paths/full-stack-ruby-on-rails/courses/ruby-programming/lessons/pattern-matching)
