Bradley Kaiser
Bradley Kaiser

Reputation: 774

What is the idiomatic way to both filter items out of a list and count them in Scala

I find that I often end up with a list of Options (or Eithers or Trys) and I want to count the number of Nones before I flatten the list. Is there a nice idiomatic way to do this that doesn't require I process the list multiple times?

Something like this but better:

val sprockets: List[Option[Sprockets]] = getSprockets()
println("this many sprockets failed to be parsed" + sprockets.filter(_.isEmpty).count) 
println(sprockets.flatten)

Upvotes: 1

Views: 130

Answers (3)

Prit
Prit

Reputation: 36

I would have used a fold as Daenyth suggested, for example somthing like this:

  val list = List(Some(1),None,Some(0),Some(3),None)

  val (flatList,count) = list.foldLeft((List[Int](),0)){
    case ((data,count), Some(x)) => (data :+ x, count)
    case ((data,count), None) => (data, count +1)
  }

  //output
  //flatList: List[Int] = List(1, 0, 3)
  //count: Int = 2

Upvotes: 2

Dima
Dima

Reputation: 40510

Recursion maybe?

 @tailrec
 def flattenAndCountNones[A](in: Seq[Option[A]], out: Seq[A] = Queue.empty[A], n: Int = 0): (Seq[A], Int) = in match {
   case Nil => (out, n)
   case Some(x) :: tail => flattenAndCountNones(tail, out :+ x, n)
   case None :: tail => flattenAndCountNones(tail, out, n + 1) 
 }

Upvotes: 1

Nathan Kronenfeld
Nathan Kronenfeld

Reputation: 513

Is this what you're looking for?

val foo = List(Some(3), Some(4), None:Option[Int], Some(5), Some(6))
val (concatenatedList, emptyCount) =
  foo.map(entry =>
      (entry.toList, if (entry.isEmpty) 1 else 0)
  ).fold((List[Int](), 0))((a, b) =>
      (a._1 ++ b._1, a._2 + b._2)
  )

It is one pass, but I'm not sure if it's really any more efficient than doing it in two - the extra object creation (the Tuple2s) in this case is going to offset the extra loop in the two-pass case.

Upvotes: 0

Related Questions