Question

I'll start the ball rolling.

Given a sequence of elements, some of which can be contained multiple times, one typical requirement is to count them - in the form of a tally or histogram.

The oft-quoted solution is:

ss.groupBy(identity).mapValues(_.size)

So what other solutions exist for similar commonly encountered problems in Scala?

Was it helpful?

Solution

Using Monoids or Numerics to define sensible operations for rich classes, using implicits if necessary.

case class Money(ccy: Currency, amount : BigDecimal) {
  def +(that : Money) = { 
    require(this.currency == that.curency)
    copy(amount = this.amount + that.amount)
  }
  def abs = copy(amount = amount.abs)
}

So then let's say I have a collection of Money's and I want to sum them:

val turnover = trades.map(_.usdValue.abs).∑ //no implicit monoid :-(

But in order to do this I need to have an implicit Monoid. But of course, a zero value for a Money only makes sense if I have some currency already!

implicit def CurrencyMonoid(implicit currency : Currency) = new Monoid[Currency] {
  def zero = Money(currency, 0)
  def append(m1 : Money, m2 : Money) = m1 + m2
}

So now Scala will use both of these implicits:

implicit val usd = Currency.USD
val turnover = trades.map(_.usdValue.abs).∑ //yay for monoids :-)

OTHER TIPS

Not that I'm banging the same drum again but...

A solution for the problem where we have a number of processes which may produce a successful output, or fail with some error message. The goal is to aggregate the successful outcomes, if all processes produce a success and if one or more fails, to aggregate all error messages.

This can be solved by scalaz validation: firstly, setup some imports

scala>  import scalaz._; import Scalaz._
import scalaz._
import Scalaz._

Now let's define our "processes"

scala> def fooI(s : String) : ValidationNEL[Exception, Int] = s.parseInt.liftFailNel
fooI: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Int]

scala> def fooF(s : String) : ValidationNEL[Exception, Float] = s.parseFloat.liftFailNel
fooF: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Float]

scala> def fooB(s : String) : ValidationNEL[Exception, Boolean] = s.parseBoolean.liftFailNel
fooB: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Boolean]

Now use Applicative to aggregate the failures/successes:

scala> def attempt(ss : String*) = (fooI(ss(0)) <|**|> (fooF(ss(1)), fooB(ss(2)))) match {
 | case Success((i, f, b)) => println("Found " + i + " " + f + " " + b) 
 | case Failure(es)        => es foreach println
 | }
attempt: (ss: String*)Unit

Now let's try for some failures:

scala> attempt("a", "b", "true")
java.lang.NumberFormatException: For input string: "a"
java.lang.NumberFormatException: For input string: "b"

Now let's try for success:

scala> attempt("1", "2.3", "false")
Found 1 2.3 false

I missed several times a way to generate a cartesian product for Scala collections. In Haskell you can write

import Control.Applicative
(,) <$> [1,2,3] <*> ["a","b"]

-- [(1,"a"),(1,"b"),(2,"a"),(2,"b"),(3,"a"),(3,"b")]

The Scala solution

for(x <- List(1,2,3); y <- List("a","b")) yield (x,y)

is way too clumsy.

Shamelessly stolen from oxbow_lakes' answer to this question: Instantiating a case class from a list of parameters

Calling a method/function using a tuple to supply the arguments:

case class Foo(a: Int, b: String, c: Double)
(Foo.apply _).tupled apply (1, "bar", 3.14)

This can be used for ANY function.

If certain condition cond holds return Some(x), else return None:

Some(x) filter cond

[ picked up from Paul Phillips' post on mailing list ]

Sometimes you have to use while instead of for, and often you are collecting results:

val buf = new Listbuffer[Int]
while(cond()) {
  val x = fancyStuff() 
  buf += calculation(x)
}

I think it would be very helpful to have for while the same possibility to "yield" something as for for, removing some burr on the edge between imperative and functional style:

val buf = while(cond()) {
  val x = fancyStuff() 
} yield calculation(x)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top