I'm trying to generate dynamically a sequence of case class but I'm blocked at one point. I want to use variables in this function.
Here's an example of what I did (which is working as expected):
case class Example(first_name: String, last_name: String)
Object Example{
def createRecords(number: Int) : Seq[Example]{
Seq.fill(number)(Example("Bob", "Marley"))
}}
The thing I want to do now is to have the first_name and the last_name as variables in the generating process that would look like something like this :
Object Example{
def createRecords(number: Int) : Seq[Example]{
Seq.fill(number)(
val first_name = generateRandomFirstName()
val last_name = generateRandomLastName()
Example(first_name, last_name))
}}
Is there an easy way to be able to do that or I need to simply refactor my code and generate what I need with a standard loop ?
Thanks in advance
Your code is actually very close, you just need to replace () with {} to turn the argument into an expression:
Seq.fill(number){
val first_name = generateRandomFirstName()
val last_name = generateRandomLastName()
Example(first_name, last_name)
}
You can also just call the functions in the constructor:
Seq.fill(number)(
Example(generateRandomFirstName(), generateRandomLastName())
)
Note that Seq is a generic interface so you should probably use an explicit type such as List or Vector to make sure you are getting the type you want.
Related
I'm currently challenging myself to skill up in Scala and FP. And today:
I came up with an issue that might interest you, devil prog masters ;)
Let's say I have the following case class in scala 3:
type EmailAddress = String // I defined them like that to show I'm interested in
type PhoneNumber = String // ... attributes via their names, not via their types.
case class Person(name: String, emails: List[EmailAddress], phones: List[PhoneNumber])
I would like to have a method that automatically transform (almost) all fields.
For example, I would like to order emails with the default given instance of Ordering[String] and phones with a specified one.
Ideally I should be able to exclude name field.
So I would get something like:
/* Below, I represented the kind of parametrization I would like to be able to do
* as parameters of the method orderValues,
* but it could be annotations or meta-programming instead.
*
* An `orderedPerson` can be directly an instance of Person
* or something else like an OderedEntity[Person], I don't care so far.
*/
val orderedPerson =
person.orderValues(
excluded = Set("name"),
explicitRules = Map(
// Phones would have a special ordering (reverse is just a dummy value)
"phones" -> Ordering.String.reverse
)
)
// -----
// So we would get:
Person(
name = "Xiao",
emails = List("a#a.a", "a#a.b", "a#b.a"),
phones = List("+86 100 9000 1000", "+86 100 2000 1000")
)
I haven't used Reflection for a long time and I'm not yet familiar with Meta-Programming, but I'm open to any solution that can help me to achieve that.
It's a good opportunity for learning !
[Edit]
My intentional intent was to have a library that can be use to easily anonymize any data.
The type keyword in Scala is just a type alias. You should use a newtype library like https://github.com/estatico/scala-newtype (or opaque type in Scala 3) and derive implicit instances of Ordering from String
Example with estatico/scala-newtype:
import io.estatico.newtype.macros.newtype
import io.estatico.newtype.ops._
#newtype case class Email(string: String)
object Email {
implicit val ordering: Ordering[Email] = deriving
}
#newtype case class PhoneNumber(string: String)
object PhoneNumber {
implicit val ordering: Ordering[PhoneNumber] = deriving[Ordering].reverse
}
case class Person(name: String, emails: List[Email], phones: List[PhoneNumber]) {
lazy val orderValues: Person = this.copy(emails = emails.sorted, phones = phones.sorted)
}
Person(
"Xiao",
List(Email("a#a.a"), Email("a#a.b"), Email("a#b.a")),
List(PhoneNumber("+86 100 9000 1000"), PhoneNumber("+86 100 2000 1000"))
).orderValues
I have a database for objects called Campaigns containing three fields :
Id (int, not nullable)
Version (int, not nullable)
Stuff (Text, nullable)
Let's call CampaignsRow the corresponding slick entity class
When I select line from Campaigns, I don't always need to read stuff, which contains big chunks of text.
However, I'd very much like to work in the codebase with the class CampaignsRow instead of a tuple, and so to be able to sometimes just drop the Stuff column, while retaining the original type
Basically, I'm trying to write the following function :
//Force dropping the Stuff column from the current Query
def smallCampaign(campaigns: Query[Campaigns, CampaignsRow, Seq]): Query[Campaigns, CampaignsRow, Seq] = {
val smallCampaignQuery = campaigns.map {
row => CampaignsRow(row.id, row.version , None : Option[String])
}
smallCampaignQuery /* Fails because the type is now wrong, I have a Query[(Rep[Int], Rep[Int], Rep[Option[String]), (Int, Int, Option[String], Seq] */
}
Any idea how to do this ? I suspect this has to do with Shape in slick, but I can't find a resource to start understanding this class, and the slick source code is proving too complex for me to follow.
You're actually already doing almost what you want in def *, the default mapping. You can use the same tools in the map method. Your two tools are mapTo and <>.
As you've found, there is the mapTo method which you can only use if your case class exactly matches the shape of the tuple, so if you wanted a special case class just for this purpose:
case class CampaignLite(id: Int, version: Int)
val smallCampaignQuery = campaigns.map {
row => (row.id, row.version).mapTo[CampaignLite]
}
As you want to reuse your existing class, you can write your own convert functions instead of using the standard tupled and unapply and pass those to <>:
object CampaignRow {
def tupleLite(t: (Int, Int)) = CampaignRow(t._1, t._2, None)
def unapplyLite(c: CampaignRow) = Some((c.id, c.version))
}
val smallCampaignQuery = campaigns.map {
row => (row.id, row.version) <> (CampaignRow.tupleLite, CampaignRow.unapplyLite)
}
This gives you the most flexibility, as you can do whatever you like in your convert functions, but it's a bit more wordy.
As row is an instance of the Campaigns table you could always define it there alongside *, if you need to use it regularly.
class Campaigns ... {
...
def * = (id, version, stuff).mapTo[CampaignRow]
def liteMapping = (id, version) <> (CampaignRow.tupleLite, CampaignRow.unapplyLite)
}
val liteCampaigns = campaigns.map(_.liteMapping)
Reference: Essential Slick 3, section 5.2.1
If I understand your requirement correctly, you could consider making CampaignRow a case class that models your Campaigns table class by having Campaigns extend Table[CampaignRow] and providing the bidirectional mapping for the * projection:
case class CampaignRow(id: Int, version: Int, stuff: Option[String])
class Campaigns(tag: Tag) extends Table[CampaignRow](tag, "CAMPAIGNS") {
// ...
def * = (id, version, stuff) <> (CampaignRow.tupled, CampaignRow.unapply)
}
You should then be able to do something like below:
val campaigns = TableQuery[CampaignRow]
val smallCampaignQuery = campaigns.map( _.copy(stuff = None) )
For a relevant example, here's a Slick doc.
For example my case class is
case class Test(id: String, myValues: List[Item])
case class Item(id: Long, order: Long)
and I get string value like
val checkValue: String = "id"
I want sort Tests by items and I want it to look like
val test= Test("0", List(Item(0, 14), Item(1, 34))
val sortedItems = test.myValues.map(_.*checkValue*).sorted
Its about get field of class like someInstanceOfClass.checkValue
Scala is not an interpreted language, therefore you can't just use strings as variable names. The easiest way to solve your problem is to map the string value to the variable:
scala> def get(item: Items, str: String) = str match {
| case "id" => item.id
| case "order" => item.order
| }
get: (item: Items, str: String)Long
scala> test.myValues.map(get(_, checkValue)).sorted
res0: List[Long] = List(0, 1)
scala> test.myValues.map(get(_, "order")).sorted
res1: List[Long] = List(14, 34)
Of course there are more ways to solve the problem. You could use Reflection to read the name of the variable at runtime. In case you already know at compile time the name of the variable you want to read, you could also use macros to generate the code that is doing what you want. But these are both very specialized solutions, I would go with the runtime matching as shown above.
You may wish to rethink how you're going about this. What good does the string "id" actually do you? If you just need the capability to pull out a particular bit of data, why not use a function?
val f: Item => Long = _.id
Do you not want to have to type the function type over and over again? That's fine too; you can use a method to request the compiler's help filling in the type arguments:
def pick[A](x: Item => A) = x
val f = pick(_.id)
Now you can use f anywhere you would have used "id". (You can even name it id instead of f if that will help, or something that reminds you that it's actually a function that gets an id, not an id itself, like idF or getId.)
I am analyzing some Scala code written in a codebase written by others and I am trying to refactor the code.
What I want to accomplish is: Display the value of the statusUpdate variable, and also capture its internal variables like taskRun.filesFound.
I want to see what is being passed into the statusUpdate varible upstream to some other code, before it is written into that ChannelBuffer with the copiedBuffer method.
So, the code description is as below and in that code there is a method that is is defined in a trait which is in turn is mixed into some other class:
def sendApiUpdate(tasks:Map[String,FileTaskState]) = future{
val statusUpdate = MyTaskStatus(tasks.map(tuple=>
(tuple._1, TaskStatus(
tuple._2.task.name,
tuple._2.taskId,
tuple._2.task.taskState.dispName,
tuple._2.taskRun.trawledTime,
tuple._2.taskRun.filesDiscovered,
tuple._2.taskRun.filesWorkedOn
))).toMap)
dispatch(someUrl, ChannelBuffer.copiedBuffer(write(statusUpdate).getBytes("UTF-8") ))
}
where MyTaskStatus is a case class as below:
case class MyTaskStatus(taskMap:Map[String, TaskStatus])
tasks is:
tasks: Map[String, FileTaskState]
and FileTaskState is a case class as follows:
case class FileTaskState(trawler: ActorRef, taskId: String, runId: String, taskRun)
//there are more field values but I left them out
filesDiscovered is:
def filesDiscovered = noFilesDiscovered.getOrElse(0L)
Also, in the following line of code:
tuple._2.taskRun.filesDiscovered
taskRun is one of the fields of case class FileTaskState
taskRun is:
taskRun: TaskRun
and TaskRun is a case class described as below:
case class TaskRun( taskId: String, noFilesDiscovered)
Update:
I would like to extract the values associated with the tuples in the tasks.map(...) in their separate variable.
for example, I would like to extract tuple._2.taskRun.filesDiscovered into: val filesDisc = <>
The purpose is to help me with refactoring.
Thanks in advance
My intention is to create a function in Scala which accepts some kind of a dynamic query (similar to case expressions in pattern matching) and returns matching instances inside a List. Suppose that the list consists of instances of case class Person which has few properties with different types. The function should be able to accept a dynamic combination of values for some of the fields, and return matching Persons. I am specifically looking for a clean solution. One possible ways to use such a function would be to pass an object with an anonymous type as the query (the "pattern"):
def find(?): List<Person> = { ? }
val matches = find(new { val name = "Name"; val gender = Gender.MALE })
Ideally, I would like to develop a clean way of passing conditions, instead of concrete values, but it's not essential. Since I am learning Scala, I am not aware of all the techniques to implement such a thing. In C#, I used an anonymous type (similar to the second line of code above) and dynamic parameters to achieve something similar. What is a clean and elegant solution in Scala?
I'm not sure if this is what you are looking for but let's try it this way:
First, we define Person as case class Person(name: String, gender: Gender.Value) where Gender is an already defined enum.
Then we create a Query case class which has the same fields, but as options which default to None, and a method for comparing the query to a person:
case class Query(name: Option[String] = None,
gender: Option[Gender.Value] = None){
def ===(person: Person) = check(person.name, name) &&
check(person.gender, gender)
private def check[T](field: T, q: Option[T]) = field == q.getOrElse(field)
}
Unfortunately, in this solution === has to call check separately for each field. Let's leave it like that for now. Maybe it is sufficient (because, for example, the list of fields will not change).
Note that check returns true if the query's option is None, sot you don't have to pass all fields of the query:
val q = Query(name = Some("Ann")) // the gender is not important
q === Person("Ann", Gender.FEMALE) // returns true
And finally the find method:
def find(people: List[Person], query: Query) = people.filter(query === _)