generate list of case class with int field without repeat

172 Views Asked by At

I want to generate a List of some class which contains several fields. One of them is Int type and it doesn’t have to repeat. Could you help me to write the code?

I tried next:

  case class Person(name: String, age: Int)

  implicit val genPerson: Gen[Person] = 
    for {
    name <- arbitrary[String]
    age <- Gen.posNum[Int]
    } yield Person(name, age)

  implicit val genListOfPerson: Gen[scala.List[Person]] = Gen.listOfN(3, genPerson)

The problem is that I got an instance of a person with equal age.

2

There are 2 best solutions below

0
Levi Ramsey On BEST ANSWER

If you're requiring that no two Persons in the generated list have the same age, you can

implicit def IntsArb: Arbitrary[Int] = Arbitrary(Gen.choose[Int](0, Int.MaxValue))
implicit val StringArb: Arbitrary[String] = Arbitrary(Gen.listOfN(5, Gen.alphaChar).map(_.mkString))
implicit val PersonGen = Arbitrary(Gen.resultOf(Person.apply _))
implicit val PersonsGen: Arbitrary[List[Person]] =
  Arbitrary(Gen.listOfN(3, PersonGen.arbitrary).map { persons =>
    val grouped: Map[Int, List[Person]] = persons.groupBy(_.age)
    grouped.values.map(_.head) // safe because groupBy
  })

Note that this will return a List with no duplicate ages but there's no guarantee that the list will have size 3 (it is guaranteed that the list will be nonempty, with size at most 3).

If having a list of size 3 is important, at the risk of generation failing if the "dice are against you", you can have something like:

def uniqueAges(persons: List[Person], target: Int): Gen[List[Person]] = {
  val grouped: Map[Int, List[Person]] = persons.groupBy(_.age)
  val uniquelyAged = grouped.values.map(_.head)
  val n = uniquelyAged.size
  if (n == target) Gen.const(uniquelyAged)
  else {
    val existingAges = grouped.keySet
    val genPerson = PersonGen.arbitrary.retryUntil { p => !existingAges(p.age) }
    Gen.listOf(target - n, genPerson)
      .flatMap(l => uniqueAges(l, target - n))
      .map(_ ++ uniquelyAged)
  }
}

implicit val PersonsGen: Arbitrary[List[Person]] =
  Arbitrary(Gen.listOfN(3, PersonGen.arbitrary).flatMap(l => uniqueAges(l, 3)))
0
Zvi Mints On

You can do it as follows:

implicit def IntsArb: Arbitrary[Int] = Arbitrary(Gen.choose[Int](0, Int.MaxValue))
implicit val StringArb: Arbitrary[String] = Arbitrary(Gen.listOfN(5, Gen.alphaChar).map(_.mkString))
implicit val PersonGen = Arbitrary(Gen.resultOf(Person.apply _))
implicit val PersonsGen: Arbitrary[List[Person]] = Arbitrary(Gen.listOfN(3, PersonGen.arbitrary))