removing elements from a sequence using for/yield - scala

Given a Future[Seq[Widget]], where Widget contains a amount : Int property, I'd like to return a Seq[Widget] but for only those Widgets whose amount value is greater than 100. I believe the for { if … } yield { } construct will give me what I want but am unsure how to filter through the Sequence. I have:
val myWidgetFuture : Future[Seq[Widget]] = ...
for {
widgetSeq <- myWidgetFuture
if (??? amount > 100) <— what to put here?
} yield {
widgetSeq
}
If there's a clean non-yield way of doing this that will also work for me.

You don't even need yield. Use map.
val myWidgetFuture: Future[Seq[Widget]] = ???
myWidgetFuture map { ws => ws filter (_.amount > 100) }

If you want to use for … yield with an if filter, you'll need to use two fors:
for {
widgetSeq <- myWidgetFuture
} yield for {
widget <- widgetSeq
if widget.amount > 100
} yield widget

Related

When to use keyword return in Scala

I learned early on that there is no reason to use the return keyword in Scala (as far as I'm aware). That being said I found an example where simply changing adding the return keyword made my function work, where it previously didn't.
The code in question comes from my solution to the Advent of Code day 7 challenge.
def containsShinyGoldBag(bagContents: Map[String, List[String]], currentBag: String): Boolean = {
val contents = bagContents(currentBag)
if (bagContents(currentBag).contains("shiny gold") ) {
// Base Case: Bag Found in list of bags
true
} else if (contents == List.empty){
// Base Case: Dead End
false
} else {
// Continue searching down list
// Ideal solution ( gives same result as the working solution without return keyword )
// for (b <- contents) containsShinyGoldBag(bagContents, b)
// Working solution
for (b <- contents) {
if (containsShinyGoldBag(bagContents, b)) {
println(s"Found one! $b inside a $currentBag")
return true // <--- culprit
}
else false
}
false
}
}
// In the main function
var count = 0
for (bag <- bagContents.keys) {
if (containsShinyGoldBag(bagContents, bag)) {
count = count + 1
}
}
println(s"There are $count way to bring a shiny gold bag!")
When I run the code without return I end up with count = 7, which is the number of bags directly containing a shiny gold bag, rather than the correct number which counts bags that contain a shiny gold bag somewhere inside of one of their other bags down the line.
A function returns the value of the last expression it evaluates; in your case that will be one of:
true after if (bagContents(currentBag).contains("shiny gold") );
false after else if (contents == List.empty);
the last false.
true is not in such a position, so you need return to, well, make the function return it. Otherwise it's evaluated and ignored because you don't do anything with it. So is else false in the same for, actually, it can be removed without changing the meaning.
The alternative to avoid return here is
contents.exists(b => containsShinyGoldBag(bagContents, b))

How to pass values instead of Vector in foreach loop?

My Reponse is:
[
{"id":106455,"assetId":482282,"masterKeyframeId":157060,"closed":false},
{"id":106661,"assetId":502174,"masterKeyframeId":169193,"closed":false}
{.....and so many...}
]
I have fetched "assetId" and "masterKeyframeId" correctly using below request, but the problem is how do I set both values in foreach loop? (I have used "aid" and it fetches single value but don't know about passing single value for "mkeyframeId" as it takes Vector)
.exec(http("request_7")
.get(uri3 + "/sortBy=SEGGREGATED_SESSION_SORT;reviewState=IN_PROGRESS")
.check(jsonPath("$..assetId").findAll.saveAs("astId"))
.check(jsonPath("$..masterKeyframeId").findAll.saveAs("mkeyframeId"))
.headers(headers_7)
)
.foreach("${astId}", "aid") {
doIf(session => session("aid").as[String] != "-1")
{
exec(http("Set_IDs")
.get("/a/" + accountname + "/assets/${aid}/keyframe/${mkeyframeId}")
)
}
}
Here the problem is ${mkeyframeId} it takes vector and pass in url like this,
Sending request=Set_IDs uri=https://qa1.net/a/hbmin1ac/assets/482282/keyframe/Vector(157060,%20169193):
Instead of
https://qa1.net/a/hbmin1ac/assets/482282/keyframe/157060
https://qa1.net/a/hbmin1ac/assets/502174/keyframe/169193
Thanks.
You should try something like this:
.foreach("${astId}", "aid", "counter") {
doIf { session =>
for {
aid <- session("aid").validate[String]
} yield aid != "-1"
} {
exec(http("Set_IDs")
.get { session =>
for {
aid <- session("aid").validate[String]
mkeyframeId <- session("mkeyframeId").validate[Seq[String]]
c <- session("counter").validate[Int]
} yield s"/a/$accountname/assets/$aid/keyframe/${mkeyframeId(c)}"
}
)
}
}
You can write the doIf block like this too:
doIf( _("aid").validate[String].map(_ != "-1"))

How can I reduce the redundancies of the fields handling of feed handler

I am subscript to a message feed for a number of fields, I need to set the values from the feed to the domain object and have code like below:
if (map.contains(quoteBidPriceAcronym)) {
quote.bid.price = Some(map.get(quoteBidPriceAcronym).get.asInstanceOf[Number].doubleValue());
quote.changed = true;
}
if (map.contains(quoteBidSizeAcronym)) {
quote.bid.size = Some(sizeMultipler() * map.get(quoteBidSizeAcronym).get.asInstanceOf[Number].intValue());
quote.changed = true;
}
if (map.contains(quoteBidNumAcronym)) {
quote.bid.num = Some(map.get(quoteBidNumAcronym).get.asInstanceOf[Number].shortValue());
quote.changed = true;
}
if (map.contains(quoteAskPriceAcronym)) {
quote.ask.price = Some(map.get(quoteAskPriceAcronym).get.asInstanceOf[Number].doubleValue());
quote.changed = true;
}
if (map.contains(quoteAskSizeAcronym)) {
quote.ask.size = Some(sizeMultipler() * map.get(quoteAskSizeAcronym).get.asInstanceOf[Number].intValue());
quote.changed = true;
}
if (map.contains(quoteAskNumAcronym)) {
quote.ask.num = Some(map.get(quoteAskNumAcronym).get.asInstanceOf[Number].shortValue());
quote.changed = true;
}
if (map.contains(quoteExchTimeAcronym)) {
quote.exchtime = getExchTime(String.valueOf(map.get(quoteExchTimeAcronym).get));
}
It look pretty redundant, any suggestion to improve it?
You can do something like:
map.get(quoteBidPriceAcronym).map { item =>
quote.bid.price = item.map(_.asInstanceOf[Number].doubleValue())
quote.changed = true
}
Other issues might be better to fix outside. E.g. why map[quoteBidPriceAcronym] is storing an Option, if your code assumes it's not going to be None?
Something like this perhaps?
val handlers = Map[String, Number => Unit] (
quoteBidPriceAcronym -> { n => quote.bid.price = Some(n.doubleValue) },
quoteBidSizeAcronym -> { n => quote.bid.size = Some(sizeMultipler() * n.intValue },
etc. ...
)
for {
(k,handler) <- handlers
values <- map.get(k).toSeq
quote.chanded = true
_ = handler(n.asInstanceof[Number])
}
Personally, I don't like code changing an object state (quote) but this is a question on Scala, not functional programming.
That said I would reverse the way you are using you map map keys. Instead of checking whether a value exists to perform some action, I'd have a map from your keys to actions and I'd iterate over your map elements.
e.g (assuming map is of the type Map[String, Any]):
val actions: Map[String, PartialFunction[Any, Unit]] = Map(
(quoteBidPriceAcronym, {case n: Number => quote.bid.price = Some(n.doubleValue())}),
(quoteBidSizeAcronym, {case n: Number => quote.bid.size = Some(sizeMultipler() * n.doubleValue())}),
...
...
)
for((k,v) <- map; action <- actions.get(k); _ <- action.lift(v))
quote.changed = true;
The for construct here iterates over map key-values, then (next level of iteration, over the possible action available for the key. If an action is found, which is a partial function, it gets lifted to make it a function from Any to Option[Unit]. That way, you can iterate in an additional inner level so quote.changed = true is only run when the action is defined for v.

Converting Imperative Expressions to Functional style paradigm

I have the following Scala snippet from my code. I am not able to convert it into functional style. I could do it at other places in my code but not able to change the below one to functional. Issue is once the code exhausts all pattern matching options, then only it should send back "NA". Following code is doing that, but it's not in functional style (for-yield)
var matches = new ListBuffer[List[String]]()
for (line <- caselist){
var count = 0
for (pat <- pattern if (!pat.findAllIn(line).isEmpty)){
count += 1
matches += pat.findAllIn(line).toList
}
if (count == 0){
matches += List("NA")
}
}
return matches.toList
}
Your question is not entirely complete, so I can't be sure, but I believe the following will do the job:
for {
line <- caselist
matches = pattern.map(_.findAllIn(line).toList)
} yield matches.flatten match {
case Nil => List("NA")
case ms => ms
}
This should do the job. Using foreach and filter to generate the matches and checking to make sure there is a match for each line will work.
caseList.foreach{ line =>
val results = pattern.foreach ( pat => pat.findAllIn(line).toList )
val filteredResults = results.filter( ! _.isEmpty )
if ( filteredResults.isEmpty ) List("NA")
else filteredResults
}
Functional doesn't mean you can't have intermediate named values.

Getting scala's template loop index in playframework

I am trying to iterate in a playframework view, but without success for now. I have the following structure:
#if(list != null) {
for(a <- 0 to list.size()/5)
{
// some html, where I want to get the value of a
for(b <- a*5 to a*5+5) // here I want to use the a value again
{
some html
}
}
So my question is how to get the current index of the loop so that I will be able to use it.
You should combine it in one for loop:
#if(list != null) {
#for{a <- 0 to list.size()/5
b <- a*5 to a*5+5}
yield html
}
}
And use option instead of null checking.
Also you can use map function to transform your list. See details in Play documentation - http://www.playframework.com/documentation/2.0/ScalaTemplates