Any clean way to combine find and instanceof in Scala?

I want to find in some Iterable some elements that both correspond to a specific type, and checks the predicate taking this type as an argument.

I wrote this method using imperative-style programming, which seems to fit my expectations. Is there a way to write this in a more "scalaesque" way?

def findMatch[T](it: Iterable[_], clazz: Class[T], pred: T => Boolean): Option[T] = { val itr = it.iterator var res: Option[T] = None while (res.isEmpty && itr.hasNext) { val e = itr.next() if (clazz.isInstance(e) && pred(clazz.cast(e))) { res = Some(clazz.cast(e)) } } res } 
+4
source share
4 answers

If you divide your problem into subtasks, you can find a more idiomatic version. Do you want to

  • find all instances of T in Iterable[Any]
  • add them to T to make the compiler happy
  • find the first matching element

For the first point, you can easily use the filter method on Iterator . So you have

 it.iterator.filter(x => clazz.isInstance(x)) 

which returns you an Iterator[Any] that contains only T s. Now convince the compiler:

 it.iterator.filter(x => clazz.isInstance(x)).map(x => x.asInstanceOf[T]) 

Ok, now you have Iterator[T] - so you just need to find the first element that executes your predicate:

 def findMatch[T](it: Iterable[Any], clazz: Class[T], pred: T => Boolean): Option[T] = it.iterator.filter(x => clazz.isInstance(x)) .map(x => x.asInstanceOf[T]) .find(pred) 
+1
source

You can use collect if you want find and then map .

 scala> val it: Iterable[Any] = List(1,2,3,"4") it: Iterable[Any] = List(1, 2, 3, 4) scala> it.view.collect{case s: String => s}.headOption res1: Option[String] = Some(4) 
+17
source

You can work with an existing X forSome{typeX} type X forSome{typeX} instead of using _ as a type parameter. This will allow you to write it with the specified search method and use the map method in the Option type:

 def findMatch[T](it: Iterable[X forSome {type X}], clazz: Class[T], pred: T => Boolean): Option[T] = { it.find{ e => clazz.isInstance(e) && pred(clazz.cast(e))}.map{clazz.cast(_)} } 
+1
source

You can use the Iterable find method and pattern matching with guard:

 scala> val it: Iterable[Any] = List(1,2,3,"4") it: Iterable[Any] = List(1, 2, 3, 4) scala> it.find { _ match { case s: String if s == "4" => true case _ => false }}.asInstanceOf[Option[String]] res0: Option[String] = Some(4) 

For template matching, see: http://programming-scala.labs.oreilly.com/ch03.html

0
source

All Articles