I have a case class and am trying to test similar to this. The change would be something like this...
case class Record(names: Array[Name] ...)
I am new to Scala and not sure how this would work syntactically
Please consider the following code:
case class Name(first: String, middle: String, last: String)
case class Record(names: Array[Name])
val rec = Record(
Array(Name("Sally", "Anna", "Jones"), Name("Sally1", "Anna1", "Jones1"))
)
inside (rec) { case Record(nameArray) =>
inside (nameArray) { case Array(name, name1) =>
inside(name) {
case Name(first, middle, last) =>
first should be("Sally")
middle should be("Anna")
last should be("Jones")
}
inside(name1) {
case Name(first, middle, last) =>
first should be("Sally1")
middle should be("Anna1")
last should be("Jones1")
}
}
}
Note that if the number of names at case Array(name, name1) is different then the actual, the test will fail.
As Luis mentioned in the comment, it is not recommended to use Arrays in case classes. This code will work the same if you change Array into List, Vector or ArraySeq.
Related
i have a list of the following scala trait. How can i separate the list into two, one containing only ValidatedSbcCommand objects and other only containing FailedValidationSbcCommand objects?
sealed trait SbcCommandorOrValidationError
case class ValidatedSbcCommand(sbcCommand: SbcCommand) extends SbcC ommandorOrValidationError
case class FailedValidationSbcCommand(sbcCommandError: SbcCommandError) extends SbcCommandorOr
Use the partition method on list. It takes a predicate and produces a (List, List) The first list is for the true case the second is for false.
val result = originalList.foldRight(Tuple2(List[ValidatedSbcCommand](), List[FailedValidationSbcCommand]())){ (start, rest) =>
start match {
case a:ValidatedSbcCommand => (a::rest._1, rest._2)
case b:FailedValidationSbcCommand => (rest._1, b::rest._2)
case _ => rest
}
}
Then result._1 will give you a list of ValidatedSbcCommand, and result._2 will give you a list of FailedValidationSbcCommand.
I prefer using partition with pattern matching. Given list is of type List[SbcCommandorOrValidationError] and contains only ValidatedSbcCommands and FailedValidationSbcCommands, you can do this:
val (validatedCommands, failedCommands) = list.partition {
case command: ValidatedSbcCommand => true
case _ => false
}
This will return a tuple of type (List[SbcCommandorOrValidationError], List[SbcCommandorOrValidationError]) where the first list is all the ValidatedSbcCommands and the second is all the FailedValidationSbcCommands.
If you need to access the specific subclass later on, don't cast. Use pattern matching as above:
validatedCommands.map {
case c: ValidatedSbcCommand => functionTakingValidatedSbcCommandsOnly(c)
}
From Scala 2.13, you can use of partitionMap, which does exactly what you want, keeping the subtype info:
list partitionMap {
case v: ValidatedSbcCommand => Left(v)
case f: FailedValidationSbcCommand => Right(f)
}
Some nested case classes and the field addresses is a Seq[Address]:
// ... means other fields
case class Street(name: String, ...)
case class Address(street: Street, ...)
case class Company(addresses: Seq[Address], ...)
case class Employee(company: Company, ...)
I have an employee:
val employee = Employee(Company(Seq(
Address(Street("aaa street")),
Address(Street("bbb street")),
Address(Street("bpp street")))))
It has 3 addresses.
And I want to capitalize the streets start with "b" only. My code is mess like following:
val modified = employee.copy(company = employee.company.copy(addresses =
employee.company.addresses.map { address =>
address.copy(street = address.street.copy(name = {
if (address.street.name.startsWith("b")) {
address.street.name.capitalize
} else {
address.street.name
}
}))
}))
The modified employee is then:
Employee(Company(List(
Address(Street(aaa street)),
Address(Street(Bbb street)),
Address(Street(Bpp street)))))
I'm looking for a way to improve it, and can't find one. Even tried Monocle, but can't apply it to this problem.
Is there any way to make it better?
PS: there are two key requirements:
use only immutable data
don't lose other existing fields
As Peter Neyens points out, Shapeless's SYB works really nicely here, but it will modify all Street values in the tree, which may not always be what you want. If you need more control over the path, Monocle can help:
import monocle.Traversal
import monocle.function.all._, monocle.macros._, monocle.std.list._
val employeeStreetNameLens: Traversal[Employee, String] =
GenLens[Employee](_.company).composeTraversal(
GenLens[Company](_.addresses)
.composeTraversal(each)
.composeLens(GenLens[Address](_.street))
.composeLens(GenLens[Street](_.name))
)
val capitalizer = employeeStreeNameLens.modify {
case s if s.startsWith("b") => s.capitalize
case s => s
}
As Julien Truffaut points out in an edit, you can make this even more concise (but less general) by creating a lens all the way to the first character of the street name:
import monocle.std.string._
val employeeStreetNameFirstLens: Traversal[Employee, Char] =
GenLens[Employee](_.company.addresses)
.composeTraversal(each)
.composeLens(GenLens[Address](_.street.name))
.composeOptional(headOption)
val capitalizer = employeeStreetNameFirstLens.modify {
case 'b' => 'B'
case s => s
}
There are symbolic operators that would make the definitions above a little more concise, but I prefer the non-symbolic versions.
And then (with the result reformatted for clarity):
scala> capitalizer(employee)
res3: Employee = Employee(
Company(
List(
Address(Street(aaa street)),
Address(Street(Bbb street)),
Address(Street(Bpp street))
)
)
)
Note that as in the Shapeless answer, you'll need to change your Employee definition to use List instead of Seq, or if you don't want to change your model, you could build that transformation into the Lens with an Iso[Seq[A], List[A]].
If you are open to replacing the addresses in Company from Seq to List, you can use "Scrap Your Boilerplate" from shapeless (example).
import shapeless._, poly._
case class Street(name: String)
case class Address(street: Street)
case class Company(addresses: List[Address])
case class Employee(company: Company)
val employee = Employee(Company(List(
Address(Street("aaa street")),
Address(Street("bbb street")),
Address(Street("bpp street")))))
You can create a polymorphic function which capitalizes the name of a Street if the name starts with a "b".
object capitalizeStreet extends ->(
(s: Street) => {
val name = if (s.name.startsWith("b")) s.name.capitalize else s.name
Street(name)
}
)
Which you can use as :
val afterCapitalize = everywhere(capitalizeStreet)(employee)
// Employee(Company(List(
// Address(Street(aaa street)),
// Address(Street(Bbb street)),
// Address(Street(Bpp street)))))
Take a look at quicklens
You could do it like this
import com.softwaremill.quicklens._
case class Street(name: String)
case class Address(street: Street)
case class Company(address: Seq[Address])
case class Employee(company: Company)
object Foo {
def foo(e: Employee) = {
modify(e)(_.company.address.each.street.name).using {
case name if name.startsWith("b") => name.capitalize
case name => name
}
}
}
Say, there is a case class
case class MyCaseClass(a: Int, b: String)
and an Option[MyCaseClass] variable
val myOption: Option[MyCaseClass] = someFunctionReturnOption()
Now, I want to map this Option variable like this:
myOption map {
case MyCaseClass(a, b) => do some thing
}
It seems the compiler reports error like It needs Option[MyCaseClass], BUT I gave her MyCaseClass, bla bla... How to use pattern match in Optional case class ?
Consider extracting the Option value like this,
myOption map {
case Some(MyCaseClass(a, b)) => do some thing
case None => do something else
}
or else use collect for a partial function, like this
myOption collect {
case Some(MyCaseClass(a, b)) => do some thing
}
Update
Please note that as commented, the OP code is correct, this answer addresses strictly the last question How to use pattern match in Optional case class ?
MyOption match {
Some(class) => // do something
None => // do something.
}
Or
MyOption map (class =>//do something)
Is there a nice way to check that a pattern match succeeds in ScalaTest? An option is given in scalatest-users mailing list:
<value> match {
case <pattern> =>
case obj => fail("Did not match: " + obj)
}
However, it doesn't compose (e.g. if I want to assert that exactly 2 elements of a list match the pattern using Inspectors API). I could write a matcher taking a partial function literal and succeeding if it's defined (it would have to be a macro if I wanted to get the pattern in the message as well). Is there a better alternative?
I am not 100% sure I understand the question you're asking, but one possible answer is to use inside from the Inside trait. Given:
case class Address(street: String, city: String, state: String, zip: String)
case class Name(first: String, middle: String, last: String)
case class Record(name: Name, address: Address, age: Int)
You can write:
inside (rec) { case Record(name, address, age) =>
inside (name) { case Name(first, middle, last) =>
first should be ("Sally")
middle should be ("Ann")
last should be ("Jones")
}
inside (address) { case Address(street, city, state, zip) =>
street should startWith ("25")
city should endWith ("Angeles")
state should equal ("CA")
zip should be ("12345")
}
age should be < 99
}
That works for both assertions or matchers. Details here:
http://www.scalatest.org/user_guide/other_goodies#inside
The other option if you are using matchers and just want to assert that a value matches a particular pattern, you can just the matchPattern syntax:
val name = Name("Jane", "Q", "Programmer")
name should matchPattern { case Name("Jane", _, _) => }
http://www.scalatest.org/user_guide/using_matchers#matchingAPattern
The scalatest-users post you pointed to was from 2011. We have added the above syntax for this use case since then.
Bill
This might not be exactly what you want, but you could write your test assertion using an idiom like this.
import scala.util.{ Either, Left, Right }
// Test class should extend org.scalatest.AppendedClues
val result = value match {
case ExpectedPattern => Right("test passed")
case _ => Left("failure explained here")
})
result shouldBe 'Right withClue(result.left.get)
This approach leverages the fact that that Scala match expression results in a value.
Here's a more concise version that does not require trait AppendedClues or assigning the result of the match expression to a val.
(value match {
case ExpectedPattern => Right("ok")
case _ => Left("failure reason")
}) shouldBe Right("ok")
Here is an example case class:
case class Person( firstName: Either[Unit, String],
middleName: Either[Unit, Option[String],
lastName: Either[Unit,String])
Any time I get an instance of this case class with a middleName it is invalid and I want to do something, all other cases are ok.
EDIT
To clarify. I need to guard against using an instance of this case class in a certain method if it was constructed with a middleName. So I would want to do something like this:
person match {
case Person(_,m,_) => halt()
case _ => continue()
}
I'm just having a hard time thinking about the types involved here.
Your pattern matching does not test the actual value of middleName, it simply assigns it to m. All Person instances are gonna match this first case.
If you want to call halt if middleName is a Right for example you should write:
person match {
case Person(_, Right(_), _) => halt()
case _ => continue()
}
If you want to dive into the value of Right to see if it's a Some:
person match {
case Person(_, Right(Some(_)), _) => halt()
case _ => continue()
}