What about methods like .flatCollect and .collectForeach?

We already have nice collection method named .collect (= .filter + .map). it receives partial function, and map values from each element.

By the way, some times we want to receive but ignore element (such as logging).

val xs = Seq(1, 2, 3, 4)
xs.collect {
  case n if n % 3 => Seq(n)
  case _ => { println("oops"); Seq() }
}.flatten

Though we can achieve it by using .flatten after .collect, unified method can be useful.

In addition, method like .filter + .foreach can be useful.

1 Like

Your example can be written with just collect:

xs.collect {
  case n if n % 3 == 0 => n
}

Elements that don’t match are dropped.

7 Likes

It’s better not to have too many different methods that are just minor variants on what we already have. In this case, flatMap provides the general capability that you want:

xs.flatMap{
  case n if (n % 3) != 0 => Seq(n)
  case _ => { println("oops"); Seq.empty }
}

The logic is plenty clear; the efficiency isn’t quite as good as flatCollect (or, really collectOrElse) would be, but at least you don’t have to worry about the dangling flatten and extra intermediate collection.

You also–because you’re acting by side-effect anyway and not producing anything new–can just do this operation with filter various different ways, e.g.:

xs.filter{ n =>
  if (n % 3) == 0 then
    println(oops)
    false
  else true
}

collect is itself already a slightly dubious improvement to flatMap / match; I don’t think we ought to map out every possible variation. Filter and foreach is even easier if you want to avoid the intermediate collection:

xs.filter(p).foreach(f)
xs.foreach( x => if p(x) then f(x) )

So–do you have use cases where the modest improvement in efficiency is (1) important and (2) you know it’s there, but (3) you don’t do better yet by using mutability, which is admittedly harder to get right but if you care enough to add a whole method to the library maybe is worth it here?

val b = Seq.newBuilder[Int]
xs.foreach{
  case n if n % 3 => b += n
  case _ => println("oops")
}
b.result

Now, if you are in your code base doing this a lot, then it might be worth writing an extension method or something to handle this. I’m just doubtful that making everyone who uses Scala collections learn these extra methods is worth it, given how close the capability is with the existing set.

9 Likes

in java there is a Stream#mapMuitli for this