hopefully this will be a simple question about library pimping (because other questions on that subject tend to generate answers beyond my current skill level).
All I want to do is map over the cross product of a collection with itself.
val distances = points.crossMap(_ distance _) // points: List[Point3d]
So I attempted to pimp Traversable
thus:
implicit def toSelfCrossMappable[A](xs: Traversable[A]) = new {
def crossMap[B](f: (A, A) => B) = xs.flatMap(a => xs.map(f(a, _)))
}
But it doesn't work (it's not doing the implicit conversion) and I don't understand why not (I'm pretty new to scala). I also tried the method suggested in Enriching Scala collections with a method which left me with:
implicit def toSelfCrossMappable[A, C[A]](xs: C[A])(implicit c: C[A] => Traversable[A]) = new SelfCrossable[A, C[A]](xs)(c)
class SelfCrossable[A, C](xs: C)(implicit c: C => Traversable[A]) {
def crossMap[B](f: (A, A) => B) = xs.flatMap(a => xs.map(f(a, _)))
}
, but that throws the same error as my (simpler looking) way.
What am I doing wrong here?
It's not pretty, but this can be done with IsTraversableLike
,
import scala.language.implicitConversions
import scala.collection.generic.{ CanBuildFrom, IsTraversableLike }
import scala.collection.GenTraversableLike
class SelfCrossMappable[A, Repr](xs: GenTraversableLike[A, Repr]) {
def crossMap[B, That](f: (A, A) => B)
(implicit
cbf: CanBuildFrom[Repr, B, That],
itl: IsTraversableLike[That] { type A = B }
) = xs.flatMap { a => itl.conversion(xs.map(f(a, _)))
}
}
implicit def toSelfCrossMappable[Repr](xs: Repr)
(implicit traversable: IsTraversableLike[Repr]) =
new SelfCrossMappable(traversable.conversion(xs))
Sample REPL session,
scala> List("foo", "foo", "bar").crossMap(_ == _)
res0: List[Boolean] = List(true, true, false, true, true, false, false, false, true)
In Scala 2.10 you can use implicit classes directly (Miles' answer uses the IsTraversableLike
helper which also requires Scala 2.10). It seems that the toSelfCrossMappable
(horrible name BTW) is not needed for standard collections. The following works for me:
import collection.generic.{CanBuildFrom, IsTraversableLike}
import collection.GenTraversableLike
implicit class CanCrossMap[A, Repr](xs: GenTraversableLike[A, Repr]) {
def crossMap[B, That](f: (A, A) => B)(
implicit cbf: CanBuildFrom[Repr, B, That],
itl: IsTraversableLike[That] { type A = B }): That =
xs.flatMap { a => itl.conversion(xs.map(f(a, _)))
}
}
Another option is to leave out IsTraversableLike
completely:
import collection.GenTraversableOnce
implicit class CanCrossMap[A, Repr](xs: GenTraversableLike[A, Repr]) {
def crossMap[B, That <: GenTraversableOnce[B]](f: (A, A) => B)(
implicit cbf: CanBuildFrom[Repr, B, That]): That =
xs.flatMap { a => xs.map(f(a, _))}
}
Example:
Vector((1.0, 2.0), (3.0, 4.0), (5.0, 6.0)).crossMap { case ((ax, ay), (bx, by)) =>
val dx = bx - ax
val dy = by - ay
math.sqrt(dx*dx + dy*dy)
}
Miles answer covers a few extra cases, where the collection is not directly a GenTraversableLike
, namely Array
and String
(try substituting Vector
for Array
in the last example).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With