I'm doing the following to map and update a list:
if (colors.map(_.id).contains(updated.id)) {
val newColorList = updated :: colors.filterNot(s => s.id == updated.id)
SomeMethod(newColorList)
}
else {
this
}
The above works ok, However, I want to add a condition such as this: If, and only if, the updated quantity is 0 then also update the enddate to todays date.
I've done the following
if (color.map(_.id).contains(updated.id) && updated.quantity == 0) {
val newColorList = updated.copy(enddate = Instant.now().toEpochMilli) :: colors.filterNot(s => s.id == updated.id)
}
else if (color.map(_.id).contains(updated.id)) {
val newColorList = updated :: colors.filterNot(s => s.id == updated.id)
SomeMethod(newColorList)
}
else {
this
}
Is there a better way to do this than multiple if / else statements
something like
val hasColor=color.map(_.id).contains(updated.id)
newColorList = (hasColor,updated.quantity) match {
case (true,0) => updated.copy(enddate = Instant.now().toEpochMilli) :: colors.filterNot(s => s.id == updated.id)
case (true,_) => updated :: colors.filterNot(s => s.id == updated.id)
otherwise => this
}
You can use collect over the colors collection.
I assume there is a simple a case class Color like this
case class Color(id:String, quantity: Int, endDate : LocalDate)
And updated is also of type Color
import java.time.LocalDate
case class Color(id:String, quantity: Int, endDate : LocalDate)
val updated = Color("one", 0, LocalDate.now)
val colors = List(
Color("one", 1, LocalDate.now().minusMonths(2)),
Color("two", 2, LocalDate.now.minusMonths(4)),
Color("three", 3, LocalDate.now.minusMonths(6))
)
colors.collect{
case e if e.id == updated.id && updated.quantity == 0 =>
updated.copy(endDate = LocalDate.now) //add element to new list, call method
case e if e.id == updated.id => updated
case e => e
}
//List(Color(one,0,2018-12-06), Color(two,2,2018-08-06), Color(two,2,2018-06-06))
Hope this helps you.
Related
At first, get all the children comments in a subquery
val getChildCommentStatQuery =
Tbcomments
.filter(row =>
row.itargetid === commentTargetId && row.iparentid =!= 0 && row.istatus === statusNormal)
.groupBy(_.iparentid)
.map { case (parentId, group) =>
// return parent commentId,the number of all the children,and the sum of like of all the children
(parentId, group.size, group.map(_.ilikecount).sum)
}
Secondly, use Table Tbcomments to left join with subquery table got in the first step.
val query =
Tbcomments
.joinLeft(getChildCommentStatQuery)
.on(_.iid == _._1)
.filter { case (comments, _) =>
// got the first level comment whose parentid equals to 0
comments.itargetid === commentTargetId && comments.iparentid === 0 && comments.istatus === statusNormal
}
.map { case (comments, childComments) =>
val replyCount = childComments.map(_._2)
val likeCountSum = comments.ilikecount + childComments.map(_._3.getOrElse(0))
val hot = replyCount + likeCountSum
val support = comments.ilikecount - comments.inotlikecount
(comments.iid, replyCount, likeCountSum, hot, support, comments.dtcreated)
}
.sortBy(row =>
sortOrder match {
case LATEST_COMMENT_ORDER => row._6.desc
case EARLIEST_COMMENT_ORDER => row._6.asc
case SUPPORT_COMMENT_ORDER => row._5.desc
// case HEAT_COMMENT_ORDER => row._4.get
})
The problem is, the row._4 is a field aggregate based on right table, and in the left join case it's an option. It seems can't sortBy the option field directly.
Thanks!
I'm new to Scala and Slick. I'm trying to run a query with a grouping by an object and creating two sequences for each object.
This is my code :
val firstQuery = {
for {
table1Id <- Table1.filter(_.someid === someid).map(_.someid)
table2 <- Table2.filter(_.someid === table1Id)
table3 <- Table3.filter(_.someId === someid)
_ <- {
Table4.filter(table4 => table4.someid === table3.someid &&
table4.name === "$$$")
}
table5 <- Table5.filter(_.someid === table2.someid)
} yield {
(table2, table3, table5)
}
}
val finalQuery = {
for {
(table6, firstQueryTuple) <- {
Table6 joinRight firstQuery on
((firstTable, secondTable) => firstTable.someid === secondTable._1.someid)
}
} yield {
(table6, firstQueryTuple._1, firstQueryTuple._2, firstQueryTuple._3)
}
}
dataBaseConnection.run(finalQuery.result).map { resultSequence =>
val grouping = resultSequence.groupBy { case (table6, table2, _, _) => (table6, table2) }
val groupingFiltered = grouping.map { case (table6table2tuple, sequence) => (table6table2tuple, sequence.map(_._4) (which is Table5), sequence.map(_._3) (which is table3)) }.toSeq
groupingFiltered match {
case tupleQuery if tupleQuery.nonEmpty => Success(tupleQuery.map(x => (x._1._2, x._1._1, x._2, x._3) ))
case _ => Failure(NonEmptyList(DBMissingRecordProblem(s"Error")))
}
}
Now what I need to achieve is this result
Seq[(Table2, Option[Table6], Seq[Table5], Seq[Table3])]
Is there a way to group by and achieve two sequences? I know it can be done for one sequence, I mean group by an object and achieve (object, Seq[anotherObject])
Please help me if you can.
val date2 = Option(LocalDate.parse("2017-02-01"))
//date1.compareTo(date2)>=0
case class dummy(val prop:Seq[Test])
case class Test(val s :String)
case class Result(val s :String)
val s = "11,22,33"
val t = Test(s)
val dt =Test("2017-02-06")
val list = dummy(Seq(t))
val list2 = dummy(Seq(dt))
val code = Option("22")
val f = date2.flatMap(c => list2
.prop
.find(d=>LocalDate.parse(d.s)
.compareTo(c)>=0))
.map(_ => Result("Found"))
.getOrElse(Result("Not Found"))
code.flatMap(c => list
.prop
.find(_.s.split(",").contains(c)))
.map(_ => Result("Found"))
.getOrElse(Result("Not Found"))
I want to && the conditions below and return Result("Found")/Result("Not Found")
d=>LocalDate.parse(d.s).compareTo(c)>=0)
_.s.split(",").contains(c)
Is there any possible way to achieve the above .In actual scenerio list and list 2 are Future
I tried to make a more realistic example based on Futures. Here is how I would do it:
val date2 = Option(LocalDate.parse("2017-02-01"))
case class Test(s: String)
case class Result(s: String)
val t = Test("11,22,33")
val dt = Test("2017-02-06")
val code = Option("22")
val f1 = Future(Seq(t))
val f2 = Future(Seq(dt))
// Wait for both futures to finish
val futureResult = Future.sequence(Seq(f1, f2)).map {
case Seq(s1, s2) =>
// Check the first part, this will be a Boolean
val firstPart = code.nonEmpty && s1.exists(_.s.split(",").contains(code.get))
// Check the second part, also a boolean
val secondPart = date2.nonEmpty && s2.exists(d => LocalDate.parse(d.s).compareTo(date2.get) >= 0)
// Do the AND logic you wanted
if (firstPart && secondPart) {
Result("Found")
} else {
Result("Not Found")
}
}
// This is just for testing to see we got the correct result
val result = Await.result(futureResult, Duration.Inf)
println(result)
As an aside, your code and date2 values in your example are Options... If this is true in your production code, then we should do a check first to see if they are both defined. If they are not then there would be no need to continue with the rest of the code:
val futureResult = if (date2.isEmpty || code.isEmpty) {
Future.successful(Result("Not Found"))
} else {
Future.sequence(Seq(f1, f2)).map {
case Seq(s1, s2) =>
val firstPart = s1.exists(_.s.split(",").contains(code.get))
val secondPart = s2.exists(d => LocalDate.parse(d.s).compareTo(date2.get) >= 0)
if (firstPart && secondPart) {
Result("Found")
} else {
Result("Not Found")
}
}
}
Use pattern matching on Option instead of using flatMap
e.g.
val x = Some("20")
x match {
case Some(i) => println(i) //do whatever you want to do with the value. And then return result
case None => Result("Not Found")
}
Looking at what you are trying to do, You would have to use pattern matching twice, that too nested one.
I was trying to insert if not exists, and get the row if it does.
I come up with this:
def saveOrGet(u: User) = (for {
userGet <- get(u.name).map(r => (r.id, r.active)).result.headOption
id <- save(u) if userGet.isEmpty
} yield {
userGet match {
case Some((id, active)) => (User(Some(id), u.name, active), false)
case None => (User(Some(id), u.name, u.active), true)
}
}).transactionally
private def get(name: String) = users.filter(_.name === name).take(1)
private def save(u: User) = users returning users.map(_.id) += u
EDIT
After making some modifications, now I get:
java.util.NoSuchElementException: Action.withFilter failed
Regards
Try using case _, because it seems that it's possible to have something empty rather than None
userGet match {
case Some((id, active)) => (User(Some(id), u.name, active), false)
case _ => (User(Some(id), u.name, u.active), true)
}
I want to do something like this (this is a made-up example to simplify my actual problem):
def findByGender(isMale: Option[Boolean]) = {
People.filter(row => row.name.isNotNull && isMale match {
case Some(true) => row.wife.isNotNull // find rows where wife column is not null
case Some(false) => row.wife.isNull // find rows where wife column is null
case None => true // select everything
})
}
This does not compile because of the last "true". Any better way to do this?
You have to make it a Column[Boolean]:
def findByGender(isMale: Option[Boolean]) = {
People.filter(row => row.name.isNotNull && isMale match {
case Some(true) => row.wife.isNotNull // find rows where wife column is not null
case Some(false) => row.wife.isNull // find rows where wife column is null
case None => LiteralColumn(true) // select everything
})
}
If you're using Slick 3.3.x you can use the following solution:
def findByGender(isMale: Option[Boolean]) =
People
.filter(_.name.isDefined)
.filterIf(isMale == Some(true))(_.wife.isDefined)
.filterIf(isMale == Some(false))(_.wife.isEmpty)
or
def findByGender(isMale: Option[Boolean]) =
People
.filter(_.name.isDefined)
.filterOpt(isMale) {
case (row, true) => row.wife.isDefined
case (row, false) => row.wife.isEmpty
}
There are 3 cases:
isMale is defined and equal Some(true)
Result SQL: select * from people when (name is not null) and (wife is not null);
isMale is defined and equal Some(false):
Result SQL: select * from people when (name is not null) and (wife is null);
isMale is empty
Result SQL: select * from people when name is not null;