Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding elements in a scala list and also know which predicate has been satisfied

I have the following problem in scala. I have to find the first element in al list which satisfies a predicate function with two conditions in OR. The problem is that I would like to get the element but also know which of the two conditions has been satisfied. Here is a simple example:

val l1 = List("A", "B", "AA", "BB")
val l2 = List("AA", "BB", "A", "B")

def c1(s: String) = s.startsWith("B")
def c2(s: String) = s.length == 2

println(l1.find(s => c1(s) || c2(s)))
println(l2.find(s => c1(s) || c2(s)))

result is:

Some(B)
Some(AA)

For the l1 case I would like to have some return value (a String for example) indicating that c1 was satisfied (c2 for the l2 case). A possible solution could be to define a var before the test and set it within the c1 and c2 functions, but I would like to find a more "functional style" solution, maybe something that return a Tuple like: (element found, condition satisfied).

Thanks in advance for the help

like image 869
Filippo Tabusso Avatar asked Feb 01 '10 10:02

Filippo Tabusso


People also ask

How do you check if a list contains a value in Scala?

contains() function in Scala is used to check if a list contains the specific element sent as a parameter. list. contains() returns true if the list contains that element. Otherwise, it returns false .

What is a predicate in Scala?

A predicate is a function that returns a Boolean . For example, to check if an Integer is even we can define the function isEven . scala> def isEven(i: Int) = i % 2 == 0 isEven: (i: Int)Boolean. It behaves as you would expect.

How do you define a list in Scala?

Scala Lists are quite similar to arrays which means, all the elements of a list have the same type but there are two important differences. First, lists are immutable, which means elements of a list cannot be changed by assignment. Second, lists represent a linked list whereas arrays are flat.


3 Answers

I'd do this:

Scala 2.8:

def find2p[T](l: List[T], p1: T => Boolean, p2: T => Boolean) = 
  l.view.map(el => (el, p1(el), p2(el))).find(t => t._2 || t._3)

Scala 2.7:

def find2p[T](l: List[T], p1: T => Boolean, p2: T => Boolean) = 
  l.projection.map(el => (el, p1(el), p2(el))).find(t => t._2 || t._3)

The view/projection ensures that the mapping will be done on-demand, instead of being applied to the whole list.

like image 153
Daniel C. Sobral Avatar answered Oct 25 '22 19:10

Daniel C. Sobral


def find[T](l1 : List[T], c1 : T => Boolean, c2 : T => Boolean) = ((None : Option[(String, T)]) /: l1)( (l, n) => l match {
    case x : Some[_] => l
    case x if c1(n) => Some("c1", n)
    case x if c2(n) => Some("c2", n)
    case _ => None
})

scala> find(l1, c1, c2)
res2: Option[(String, java.lang.String)] = Some((c1,B))

scala> find(l2, c1, c2)
res3: Option[(String, java.lang.String)] = Some((c2,AA))

Depending on your requirements you could have a parameter Map[T => Boolean, String] for the label strings to return: def find[T](l1 : List[T], fs : Map[T => Boolean, String]) or define your own operators.

This will evaluate the whole list where find aborts for the first element found.

like image 38
Thomas Jung Avatar answered Oct 25 '22 20:10

Thomas Jung


Here's a variant on Daniel's (and Retronym's) answer(s).

If you just want the predicate (out of a list) that succeeded, then you can use

def findP[T](list: Iterable[T], preds: Iterable[T=>Boolean]) = {
  list.view.map( x => (x , preds.find( _(x) )) ).find( _._2.isDefined )
}

Alternatively, you could use a list of named predicates:

def findP[T](list: Iterable[T],preds: Iterable[(T=>Boolean,String)]) = {
  list.view.map(x => (x , preds.find( _._1(x) ))).find( _._2.isDefined )
}

scala> findP(
     |   List(1,2,3,4,5,6),
     |   List( ((i:Int)=>i>4,"Fred") , ((i:Int)=>(i%6)==0,"Barney"))
     | )
res2: Option[(Int, Option[((Int) => Boolean, String)])] =
  Some((5,Some((<function1>,Fred))))

The result a little cluttered, but can be unwrapped easily enough to give exactly what you asked for:

def findP[T](list: Iterable[T],preds: Iterable[(T=>Boolean,String)]) = {
  list.view.map(x => (x , preds.find( _._1(x) ))).find( _._2.isDefined ) match {
    case Some((i,Some((_,s)))) => Some((i,s))
    case _ => None
  }
}

(This is code for 2.8; switch "view" to "projection" for 2.7.)

like image 44
Rex Kerr Avatar answered Oct 25 '22 18:10

Rex Kerr