# Pattern Matching with case/in — Ruby

Source: https://www.geekswithgeeks.com/en/ruby/m-pattern

> Destructure hashes and arrays and validate data shapes with pattern matching.

## Matching structure, not just values

Ruby 3 added **pattern matching** with `case ... in`. Unlike `case/when`, which compares values, `in` patterns **match structure** and **bind variables**: array patterns (`in [first, *rest]`), hash patterns (`in { status: "paid", total: Integer => total }`), alternative patterns (`in :paid | :shipped`), value patterns using `===` (ranges, classes, regexes), **guards** (`in { total: } if total > 1000`), and the pin operator `^` to compare with an existing variable. Hash patterns match if the listed keys are present, ignoring extra keys (use `**nil` to require no others). If no pattern matches and there is no `else`, Ruby raises **`NoMatchingPatternError`**, which helps catch unexpected data. The one-line forms `data => { name: }` (destructure or raise) and `data in { name: String }` (returns true or false) are handy for parsing JSON. Your own classes can participate by defining **`deconstruct`** (for array patterns) and **`deconstruct_keys`** (for hash patterns); `Data` and `Struct` objects support both automatically.

## Parsing API responses with patterns

Structure checks and variable binding in one step.

```ruby
require "json"

def describe(response)
  case response
  in { status: "ok", data: { orders: [] } }
    "No orders yet"
  in { status: "ok", data: { orders: [{ id:, total: }, *rest] } }
    "First order #{id} (Rs #{total}) and #{rest.size} more"
  in { status: "error", code: 400..499 => code, message: }
    "Client error #{code}: #{message}"
  in { status: "error", code: }
    "Server error #{code}"
  end
end

json = '{"status":"ok","data":{"orders":[{"id":"o-1","total":1200},{"id":"o-2","total":300}]}}'
puts describe(JSON.parse(json, symbolize_names: true))   # First order o-1 (Rs 1200) and 1 more

config = { db: { host: "localhost", port: 5432 } }
config => { db: { host:, port: } }                          # destructure or raise
puts "#{host}:#{port}"

Point = Data.define(:lat, :lng)
case Point.new(lat: 18.5, lng: 73.9)
in { lat: 8..37 => lat }                                  # Data supports deconstruct_keys
  puts "Within India's latitude range: #{lat}"
end
```

## Unmatched patterns raise

A `case/in` without `else` raises `NoMatchingPatternError` when nothing matches. That is usually what you want for unexpected API data, but add an `else` branch where unknown shapes are normal.

**Quiz:** How does `case ... in` differ from `case ... when`?

- [x] in matches structure and binds variables, while when compares values with ===
- [ ] They are identical
- [ ] in only works with integers
- [ ] when supports guards but in does not

*Answer:* in matches structure and binds variables, while when compares values with ===. Pattern matching destructures arrays, hashes and objects and can bind parts to variables.
