scala assign value to object - scala

I have the following code:
def getContentComponents: Action[AnyContent] = Action.async {
contentComponentDTO.list().map(contentComponentsFuture =>
contentComponentsFuture.foreach(contentComponentFuture =>
contentComponentFuture.typeOf match {
case 5 =>
contentComponentDTO.getContentComponentText(contentComponentFuture.id.get).map(
text => contentComponentFuture.text = text.text
)
}
)
Ok(Json.toJson(contentComponentsFuture))
)
and get this error message while assigning a value:
Is there a way to solve this issue?
I thought about creating a copy but that would mean that I have do lots of other stuff later. It would be much easier for me if I could reassign the value.
thanks

Unfortunately this is not possible as contentComponentFuture.text is an immutable property so you cannot change its value like that.
The only way to "change" the values of the job object is to actually create a totally new instance and discard the old one. This is standard practice when dealing with immutable objects.
Sorry for the bad news.

Just found the solution ...
In model I have to define the Attributes as var
case class ContentComponentModel(
id: Option[Int] = None,
typeOf: Int,
name: String,
version: String,
reusable: Option[Boolean],
createdat: Date,
updatedat: Date,
deleted: Boolean,
processStepId: Int,
var text: Option[String],

Related

Scala Option and Some mismatch

I want to parse province to case class, it throws mismatch
scala.MatchError: Some(USA) (of class scala.Some)
val result = EntityUtils.toString(entity,"UTF-8")
val address = JsonParser.parse(result).extract[Address]
val value.province = Option(address.province)
val value.city = Option(address.city)
case class Access(
device: String,
deviceType: String,
os: String,
event: String,
net: String,
channel: String,
uid: String,
nu: Int,
ip: String,
time: Long,
version: String,
province: Option[String],
city: Option[String],
product: Option[Product]
)
This:
val value.province = Option(address.province)
val value.city = Option(address.city)
doesn't do what you think it does. It tries to treat value.province and value.city as extractors (which don't match the type, thus scala.MatchError exception). It doesn't mutate value as I believe you intended (because value apparently doesn't have such setters).
Since value is (apparently) Access case class, it is immutable and you can only obtain an updated copy:
val value2 = value.copy(
province = Option(address.province),
city = Option(address.city)
)
Assuming the starting point:
val province: Option[String] = ???
You can get the string with simple pattern matching:
province match {
case Some(stringValue) => JsonParser.parse(stringValue).extract[Province] //use parser to go from string to a case class
case None => .. //maybe provide a default value, depends on your context
}
Note: Without knowing what extract[T] returns it's hard to recommend a follow-up

Scala - type mismatch problem with own type (String) and Future

I have a method, which need String type as an argument:
type Identity = String
case class RequireSmth(param: Identity) extends Something
Now I call this method in more complex order:
createMe(as[List])(arg =>{ parse(RequireSmth(getAction(name, surname).map(bool => getData(surname, bool).id))) })
Parse looks like:
def parse(ob: Something)
Where:
def getAction(name: String, surname: String): Future[Boolean] = {
someObject.get(name).map(_.getSomething(surname).isPossibleToTake.getOrElse(false)) //someObject is defined in constructor and does not matter here
}
def getData: (String, Boolean) => MyObject = {
case ("Doe", true) => possible
case _ => notPossible
}
MyObject, possible and notPossible definition:
case class MyObject(id : String, name: String, surname: String)
val possible = MyObject( id = "ok", name ="John", surname = "Doe")
val notPossible = MyObject( id = "not ok", name ="John", surname = "Doe")
The problem is, when I call RequireSmth method I got an error:
type mismatch;
found: scala.concurrent.Future[String]
required: com.my.smth.Identity (which expands to) String
How can I solve this problem to return Identity (or String) instead of Future[String]?
Keep the information inside the Future like this:
getAction(name, surname).map(bool => getData(surname, bool).id).map(RequireSmth)
Just keep chaining the operations together, keeping everything inside the Future:
getAction(name, surname)
.map(bool => getData(surname, bool).id)
.map(RequireSmth) // Or x => RequireSmth(x) if necessary
.map(parse)
At some point you will get to a method that has a side-effect and returns Unit, and that will be executed when all the actions in the Future are complete.
In the unlikely event that you actually need to get the value out of the Future, use Await.result. But in most cases this will not be necessary.
You need to flip the method calls:
val res: Future[???] =
getAction(name, surname)
.map(bool => getData(surname, bool).id)
.map(RequireSmth)
.map(parse)
Note that Future[String] is not a String, it's a computation that will yield a value in the future, and that means that the entire computation stack needs to return a Future[T] as well (unless you explicitly await, which blocks and is not recommended).

zipping lists with an optional list to construct a list of object in Scala

I have a case class like this:
case class Metric(name: String, value: Double, timeStamp: Int)
I receive individual components to build metrics in separate lists and zip them to create a list of Metric objects.
def buildMetric(names: Seq[String], values: Seq[Double], ts: Seq[Int]): Seq[Metric] = {
(names, values, ts).zipped.toList map {
case (name, value, time) => Metric(name, value, time)
}
}
Now I need to add an optional parameter to both buildMetric function and Metric class.
case class Metric(name: String, value: Double, timeStamp: Int, type: Option[Type])
&
def buildMetric(names: Seq[String], values: Seq[Double], ts: Seq[Int], types: Option[Seq[Type]]): Seq[Metric]
The idea is that we some times receive a sequence of the type which if present matches the length of names and values lists. I am not sure how to modify the body of buildMetric function to create the Metric objects with type information idiomatically. I can think of a couple of approaches.
Do an if-else on types.isDefined and then zip the types with types.get with another list in one condition and leave as above in the other. This makes me write the same code twice.
The other option is to simply use a while loop and create a Metric object with types.map(_(i)) passed a last parameter.
So far I am using the second option, but I wonder if there is a more functional way of handling this problem.
The first option can't be done because zipped only works with tuples of 3 or fewer elements.
The second version might look like this:
def buildMetric(names: Seq[String], values: Seq[Double], ts: Seq[Int], types: Option[Seq[Type]]): Seq[Metric] =
for {
(name, i) <- names.zipWithIndex
value <- values.lift(i)
time <- ts.lift(i)
optType = types.flatMap(_.lift(i))
} yield {
Metric(name, value, time, optType)
}
One more option from my point of view, if you would like to keep this zipped approach - convert types from Option[Seq[Type]] to Seq[Option[Type]] with same length as names filled with None values in case if types is None as well:
val optionTypes: Seq[Option[Type]] = types.fold(Seq.fill(names.length)(None: Option[Type]))(_.map(Some(_)))
// Sorry, Did not find `zipped` for Tuple4 case
names.zip(values).zip(ts).zip(optionTypes).toList.map {
case (((name, value), time), optionType) => Metric(name, value, time, optionType)
}
Hope this helps!
You could just use pattern matching on types:
def buildMetric(names: Seq[String], values: Seq[Double], ts: Seq[Int], types: Option[Seq[Type]]): Seq[Metric] = {
types match {
case Some(types) => names.zip(values).zip(ts).zip(types).map {
case (((name, value), ts,), t) => Metric(name, value, ts, Some(t))
}
case None => (names, values, ts).zipped.map(Metric(_, _, _, None))
}
}

Converting one case class to another that is similar with additional parameter in Scala

So, the problem is in the title, but here are the details.
I have two case classes:
case class JourneyGroup(id: Option[Int] = None,
key: UUID,
name: String,
data: Option[JsValue],
accountId: Int,
createdAt: DateTime = DateTime.now,
createdById: Int,
updatedAt: Option[DateTime] = None,
updatedById: Option[Int] = None,
deletedAt: Option[DateTime] = None,
deletedById: Option[Int] = None)
and
case class JourneyGroupApi(id: Option[Int] = None,
key: UUID,
name: String,
data: Option[JsValue],
accountId: Int,
createdAt: DateTime = DateTime.now,
createdById: Int,
updatedAt: Option[DateTime] = None,
updatedById: Option[Int] = None,
deletedAt: Option[DateTime] = None,
deletedById: Option[Int] = None,
parties: Seq[Party] = Seq.empty[Party])
Background: the reason for having these two separate classes is the fact that slick does not support collections, and I do need collections of related objects that I build manually. Bottom line, I could not make it work with a single class.
What I need is an easy way to convert from one to another.
At this point, to unblock myself, I created a manual conversion:
def toJourneyGroupApi(parties: Seq[Party]): JourneyGroupApi = JourneyGroupApi(
id = id,
key = key,
name = name,
data = data,
accountId = accountId,
createdAt = createdAt,
createdById = createdById,
updatedAt = updatedAt,
updatedById = updatedById,
deletedAt = deletedAt,
deletedById = deletedById,
parties = parties
)
Which is working, but extremely ugly and requires a lot of maintenance.
One thing that I tried doing is:
convert the source object to tuple
Add an element to that tuple using shapeless
and build a target object from resulting tuple
import shapeless._
import syntax.std.tuple._
val groupApi = (JourneyGroup.unapply(group).get :+ Seq.empty[Party])(JourneyGroupApi.tupled)
But, this thing is claiming, that the result of :+ is not tuple, even though in console:
Party.unapply(p).get :+ Seq.empty[Participant]
res0: (Option[Int], model.Parties.Type.Value, Int, Int, org.joda.time.DateTime, Int, Option[org.joda.time.DateTime], Option[Int], Option[org.joda.time.DateTime], Option[Int], Seq[model.Participant]) = (None,client,123,234,2016-11-12T03:55:24.006-08:00,987,None,None,None,None,List())
What am I doing wrong? Maybe there is another way of achieving this.
Could you consider Composition?
case class JourneyGroup(
...
)
case class JourneyGroupApi(
journeyGroup: JourneyGroup=JourneyGroup(),
parties: Seq[Party] = Seq()
)
Converting a journeyGroup would just be something like JourneyGroupApi(journeyGroup,parties) and "converting" a journeyGroupApi would be a matter of accessing journeyGroupApi.journeyGroup. You could perhaps come up with names that worked better for this case. Not sure if this approach would fit the rest of your code. In particular referencing journeyGroup attributes in a journeyGroupApi will be one extra level, e.g. journeyGroupApi.journeyGroup.accountId. (This could potentially be mitigated by "shortcut" definitions on journeyGroupApi like lazy val accountId = journeyGroup.accountId.)
Inheritance might also be an approach to consider with a base case class of JourneyGroup then a normal class (not case class) that extends it with parties as the extra attribute. This option is discussed further in this SO thread.

Dealing with missing elements in Scala/Spark class

I have the following file in Hadoop
val dataset=sc.textFile("/user/hue/mycompanies1.csv")
It looks like this
CS,84,Jimmys Bistro, Jimmys
CS,90,Pauls Fish
CS,100, Happy Hardware
My scala/Spark code looks like:
case class Company (
record_type: String,
company_num: Integer,
company_name: String;,
nickname: String
)
val company = dataset.map(k=>k.split(",")).map(
k => Company(k(0).trim, k(1).toInt, k(2).trim, k(3).trim)
company.toDF().registerTempTable("company_table4")
When i try to access company RDD after i get a nullpointerexception because of the missing nickname value in the data. How do i deal with this gracefully?
Since the nickname is optional, I would change the case class to reflect that, then use one of various ways to optionally obtain the index-3 element, eg:
case class Company (
record_type: String,
company_num: Integer,
company_name: String,
nickname: Option[String]
)
val company = dataset.map(k=>k.split(",")).map(
k => Company(k(0).trim, k(1).toInt, k(2).trim, k.drop(3).headOption.map(_.trim))