Scala filter return only one (or specific number) of results - scala

What is the best Scala idiomatic approach to verify that filter returns only one results (or specific amount in that matter), and if the amount correct, to continue with it?
For example:
val myFilteredListWithDesiredOneItem = unfilteredList
.filter(x => x.getId.equals(something))
.VERIFY AMOUNT
.toList

Consider this for a list of type T,
val myFilteredListWithDesiredOneItem = {
val xs = unfilteredList.filter(x => x.getId.equals(something))
if (xs.size == n) xs.toList
else List.empty[T]
}
Not a oneliner, the code remains simple none the less.

Try a match with guards, perhaps?
list.filter(...) match {
case Nil => // empty
case a if (a.size == 5) => // five items
case b#(List(item1, item2) => // two (explicit) items
case _ => // default
}

Something like this perhaps:
Option(list.filter(filterFunc))
.filter(_.size == n)
.getOrElse(throw new Exception("wrong size!"))

Related

scala using calculations from pattern matching's guard (if) in body

I'm using pattern matching in scala a lot. Many times I need to do some calculations in guard part and sometimes they are pretty expensive. Is there any way to bind calculated values to separate value?
//i wan't to use result of prettyExpensiveFunc in body safely
people.collect {
case ...
case Some(Right((x, y))) if prettyExpensiveFunc(x, y) > 0 => prettyExpensiveFunc(x)
}
//ideally something like that could be helpful, but it doesn't compile:
people.collect {
case ...
case Some(Right((x, y))) if {val z = prettyExpensiveFunc(x, y); y > 0} => z
}
//this sollution works but it isn't safe for some `Seq` types and is risky when more cases are used.
var cache:Int = 0
people.collect {
case ...
case Some(Right((x, y))) if {cache = prettyExpensiveFunc(x, y); cache > 0} => cache
}
Is there any better solution?
ps: Example is simplified and I don't expect anwers that shows that I don't need pattern matching here.
You can use cats.Eval to make expensive calculations lazy and memoizable, create Evals using .map and extract .value (calculated at most once - if needed) in .collect
values.map { value =>
val expensiveCheck1 = Eval.later { prettyExpensiveFunc(value) }
val expensiveCheck2 = Eval.later { anotherExpensiveFunc(value) }
(value, expensiveCheck1, expensiveCheck2)
}.collect {
case (value, lazyResult1, _) if lazyResult1.value > 0 => ...
case (value, _, lazyResult2) if lazyResult2.value > 0 => ...
case (value, lazyResult1, lazyResult2) if lazyResult1.value > lazyResult2.value => ...
...
}
I don't see a way of doing what you want without creating some implementation of lazy evaluation, and if you have to use one, you might as well use existing one instead of rolling one yourself.
EDIT. Just in case you haven't noticed - you aren't losing the ability to pattern match by using tuple here:
values.map {
// originial value -> lazily evaluated memoized expensive calculation
case a # Some(Right((x, y)) => a -> Some(Eval.later(prettyExpensiveFunc(x, y)))
case a => a -> None
}.collect {
// match type and calculation
...
case (Some(Right((x, y))), Some(lazyResult)) if lazyResult.value > 0 => ...
...
}
Why not run the function first for every element and then work with a tuple?
Seq(1,2,3,4,5).map(e => (e, prettyExpensiveFunc(e))).collect {
case ...
case (x, y) if y => y
}
I tried own matchers and effect is somehow OK, but not perfect. My matcher is untyped, and it is bit ugly to make it fully typed.
class Matcher[T,E](f:PartialFunction[T, E]) {
def unapply(z: T): Option[E] = if (f.isDefinedAt(z)) Some(f(z)) else None
}
def newMatcherAny[E](f:PartialFunction[Any, E]) = new Matcher(f)
def newMatcher[T,E](f:PartialFunction[T, E]) = new Matcher(f)
def prettyExpensiveFunc(x:Int) = {println(s"-- prettyExpensiveFunc($x)"); x%2+x*x}
val x = Seq(
Some(Right(22)),
Some(Right(10)),
Some(Left("Oh now")),
None
)
val PersonAgeRank = newMatcherAny { case Some(Right(x:Int)) => (x, prettyExpensiveFunc(x)) }
x.collect {
case PersonAgeRank(age, rank) if rank > 100 => println("age:"+age + " rank:" + rank)
}
https://scalafiddle.io/sf/hFbcAqH/3

Scala: AND conjunction in Lists

So I have result: List[List[Int]] = (List(0,1), List(0,1), List(1)) and I want to get the numbers every element of the the list has in common (in this case 1) like a logical AND conjunction. How can I do that?
Edit: If an element is empty it should return an empty List because there are no values every element has in common
Intuitive way
In each sublist, filter out the elements that are contained in all sublists, then flatten and remove duplicated:
val result1 = list.flatMap(_.filter(e => list.forall(_.contains(e)))).toSet
More efficient way
Find the smallest sublist and pick out elements that are in each sublist:
val result2 = list.minBy(_.size).filter(e => list.forall(_.contains(e))).toSet
Mathematical way
Turn each sublist into a set and intersect them:
val result3 = list.map(_.toSet).reduce(_.intersect(_))
You can do it with the intersect method:
def intersection(lists: List[List[Int]]): List[Int] = {
lists.headOption match {
case Some(head) =>
lists.foldLeft(head)((acc, l) => acc.intersect(l))
case None => Nil
}
The method may be more efficient if you use it with Set instead of List
The difficulty here is to do the intersect on the empty element, in this case Set.empty . to avoid this and solve the problem more functionally we can do this
def uniqueElements(reults:List[List[Int]]):Set[Int] = {
results match {
case head1::head2::tail => head1.toSet intersect head2.toSet intersect uniqueElements(tail)
case head::Nil => head.toSet
case Nil => Set.empty[Int]
}
}

Split a list into a target element, and the rest of the list?

Let's say I have something like the following:
case class Thing(num: Int)
val xs = List(Thing(1), Thing(2), Thing(3))
What I'd like to do is separate the list into one particular value, and the rest of the list. The target value can be at any position in the list, or may not be present at all. The single value needs to be handled separately, after the other values are handled, so I can't simply use pattern matching.
What I have so far is this:
val (targetList, rest) = xs.partition(_.num == 2)
val targetEl = targetList match {
case x :: Nil => x
case _ => null
}
Is it possible to combine the two steps? Like
val (targetEl, rest) = xs.<some_method>
A note on handling order:
The reason that the target element must be handled last is that this is for use in a HTML template (Play framework). The other elements are looped through, and a HTML element is rendered for each. After that group of elements, another HTML element is created for the target element.
You can do it with pattern-matching in map, you just need multiple cases:
xs map {
case t # Thing(1) => // do something with thing 1
case t => // do something with the other things
}
To handle the OP's extra requirements:
xs map {
case t # Thing(num) if(num != 1) => // do something with things that are not "1"
case t => // do something with thing 1
}
Following produces two lists as tuples for some condition.
case class Thing(num: Int)
val xs = List(Thing(1), Thing(2), Thing(3))
val partioned = xs.foldLeft((List.empty[Thing], List.empty[Thing]))((x, y) => y match {
case t # Thing(1) => (x._1, t :: x._2)
case t => (t :: x._1, x._2)
})
//(List(Thing(3), Thing(2)),List(Thing(1)))
Try this:
val (targetEl, rest) = (xs.head, xs.tail)
It works for non-empty list. Nil case must be handled separately.
After some experimentation, I've come up with the following, which is almost what I'm looking for:
var (maybeTargetEl, rest) = xs
.foldLeft((Option.empty[Thing], List[Thing]())) { case ((opt, ls), x) =>
if (x.num == 1)
(Some(x), ls)
else
(opt, x :: ls)
}
The target value is still wrapped in a container, but at least it guarantees a single value.
After that I can do
rest map <some_method>
maybeTargetEl map <some_other_method>
If the order of the original list is important:
var (maybeTargetEl, rest) = xs.
foldLeft((Option.empty[Thing], ListBuffer[Thing]())){ case ((opt, lb), x) =>
if (x.num == 1)
(Some(x), ls)
else
(opt, lb += x)
} match {
case (opt, lb) => (opt, lb.toList)
}
#evanjdooner Your solution with fold works if target element is present only once. If you want to extract only one occurrence of target element:
def find(xs: List[T], target: T, prefix: List[T]) = xs match {
case target :: tail => (target, prefix ::: tail)
case other :: tail => find(tail, target, other :: prefix)
case Nil => throw new Exception("Not found")
}
val (el, rest) = find(xs, target, Nil)
Sorry, I can't add it as a comment.

Scala pattern match on subset of random elements in a List

Can anybody suggest a efficient style to pattern match on a subset of elements in a list
containsSlice expects order and does not work with an unordered comparator list
This is what I am looking for (an inaccurate syntactical representation to drive the point)
List(1,2,3,4,5,6) match {
case x if x.contains(List(1,3)) => do something
case x if x.contains(List(2)) => else
case _ => do something else else
}
If you can live without the pattern-match,
a single "subset of elements in a list" can be identified by checking if each element in the subset is contained in the list, like such:
if( List(3,1).forall( List(1,2,3,4,5,6).contains(_) ) ) println("do something")
List(1, 2, 3, 4, 5, 6).toSet match {
case x if Set(1, 3).subsetOf(x) => println("do something")
case x if Set(2).subsetOf(x) => println("something else")
case _ => println("another thing")
}
you mean something like this? :
def [X]containsSubset(li:List[X],li2:List[X]) =
li2 match {
case Nil => true
case hd::_ => li.contains(hd) && containsSubset(li,tl)
}

Processing Set of Sets and return a flat Iterable

val input=Set(Set("a","b"),Set("b","c"))
I want this:
Map("a"->1,"b"->2,"c"->1)
What is the best functional approach for implementing such functionality?
Using yield keyword results in nested Iterables:
output = for(firstlevel<-input) yield for(item<-firstlevel) yield item
update: incorporated the suggestion to use input.toSeq.flatten
instead of input.toSeq flatMap { _.toSeq }
convert to a single sequence of values...
input.toSeq.flatten
...group values that match...
input.toSeq.flatten groupBy { identity }
...and count
input.toSeq.flatten groupBy { identity } mapValues { _.size }
If you want to use for-comprehension and yield:
output = for{
(set,idx) <- input.zipWithIndex
item <- set
} yield (item -> idx)
The code in your last line can be simplified (but does not what you want):
output = for{
set <- input
item <- set
} yield item
Oh boy, that's so ugly...
input.foldLeft(Map[String,Int]())((m,s) =>
s.foldLeft(m)((n,t) => n + (t -> (1 + n.getOrElse(t,0)))))
[Edit]
The Collection-API needs really a method for "merging" two Maps (or did I just overlook it???), e.g.
def merge[A,B](m1: Map[A,B], m2:Map[A,B])(f: (B,B)=>B):Map[A,B] =
m1.foldLeft(m2)((m,t) =>
m + (t._1 -> m.get(t._1).map(k => f(k,t._2)).getOrElse(t._2)))
With this you could write something like:
input.map(_.map(x => x -> 1).toMap).reduceLeft(merge(_,_)(_+_))
[Edit2]
With Kevin's idea merge could be written as
def merge[A,B](m1: Map[A,B], m2:Map[A,B])(f: (B,B)=>B):Map[A,B] =
m1.keys ++ m2.keys map {k => k ->
List(m1.get(k), m2.get(k)).flatten.reduceLeft(f)} toMap
Seems like my Scala-Fu is still too weak. What's the best way to express
(o1,o2) match {
case (Some(x),Some(y)) => Some(f(x,y))
case (Some(x), _) => Some(x)
case (_, Some(y)) => Some(y)
case => error("crack in the time-space-continuum")
}
?