Reputation: 10904
Is there a more readable and and more robust (to refactoring) way to match on case classes like this ?
Very long case class with many "fields".
case class Data(name: String, time: Long, ..., userId: Option[UUID] ..., orders: Int, ... ) //more fields fields more
Works. But error prone when fields position changes. One ends up counting _
.
res match {
case data@Data(_,_,_,_,_,_,Some(id),_,_,_,6,_,_) => (id, data.orders)
case _ => ...
}
Works also. Is stable to changing orders. Gets really cumbersome with more checks in the guard. Also reading the value has to be repeated.
res match {
case data: Data if data.userId.isDefined && data.orders == 6 => (data.userId.get,data.orders)
case _ => ...
}
Is there a way to combine Variant A and B to get the benefit of both approaches ?
Upvotes: 7
Views: 1352
Reputation: 6992
As long as there is an unapply
method in scope, which takes a parameter from the type you're matching against, you can define a custom extractor for this, e.g.
case class Big(foo: String, bar: String, baz: Option[String], x: Int, y: Int)
object SmallerBig {
def unapply(x: Big): Option[(Option[String], Int)] = Some(x.baz, x.y)
}
val x = Big("a", "b", Some("c"), 1, 2)
x match {
case Big(_, _, _, _, _) =>
case SmallerBig(Some(a), b) =>
}
The return type of the unapply method must not be Option
, but the type you're return must provide the methods get
and isDefined
.
Upvotes: 2
Reputation: 170835
You can use a custom extractor:
res match {
case ExtractUserIdAndOrders(Some(id), 6) => ...
case _ => ...
}
where
object ExtractUserIdAndOrders {
def unapply(data: Data) = Some((data.userId, data.orders))
}
You can define it inside the method if you need it just once, or in a wider scope for multiple similar matches.
Upvotes: 14