पाठ 12 / 25

Pattern Matching with case/in

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.

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.

त्वरित जाँच: How does `case ... in` differ from `case ... when`?

  • 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.