5

In Clojure the diff function can be applied to maps, that doesn't seem to be the case in Scala, is anyone aware of something in Scala that would make it more accessible to obtain what the Clojure diff function obtains when it is applied to maps?

Here's the Clojure diff function explained for reference.

http://clojuredocs.org/clojure_core/clojure.data/diff

RatavaWen
  • 147
  • 1
  • 8

4 Answers4

4

This is equivalent to Clojure's diff:

import collection.generic.CanBuildFrom

def diff[T, Col](x: Col with TraversableOnce[T], y: Col with TraversableOnce[T])
        (implicit cbf: CanBuildFrom[Col, T, Col]): (Col, Col, Col) = {
  val xs = x.toSet
  val ys = y.toSet
  def convert(s: Set[T]) = (cbf(x) ++= s).result
  (convert(xs diff ys), convert(ys diff xs), convert(xs intersect ys))
}

It can operate on any kind of TraversableOnce and will return results with the same type as its parameters:

scala> diff(Map(1 -> 2), Map(1 -> 2))
res35: (scala.collection.immutable.Map[Int,Int], scala.collection.immutable.Map[Int,Int], scala.collection.immutable.Map[Int,Int]) = (Map(),Map(),Map(1 -> 2))
wingedsubmariner
  • 13,350
  • 1
  • 27
  • 52
2

As others have said there isn't something exactly like that, but you can build it anyways. Here's my attempt that is added on as a companion to the map class. It produces the same result as the clojure diff example.

object MapsDemo extends App{
  implicit class MapCompanionOps[A,B](val a: Map[A,B]) extends AnyVal {
    def diff(b: Map[A,B]): (Map[A,B],Map[A,B],Map[A,B]) = {
          (a.filter(p => !b.exists(_ == p)),  //things-only-in-a
           b.filter(p => !a.exists(_ == p)),    //things-only-in-b
           a.flatMap(p => b.find(_ == p) ))    //things-in-both
        }
  }

    val uno = Map("same" ->"same","different" -> "one")
    val dos = Map("same" ->"same","different" -> "two","onlyhere"->"whatever")
    println(uno diff dos) //(Map(different -> one),Map(different -> two, onlyhere -> whatever),Map(same -> same))
    println( Map("a"->1).diff(Map("a"->1,"b"->2)) ) //(Map(),Map(b -> 2),Map(a -> 1))
}
Gangstead
  • 4,152
  • 22
  • 35
1

You can achieve that by converting the maps to list first. For example:

scala> val a = Map(1->2, 2-> 3).toList
scala> val b = Map(1->2, 3-> 4).toList
scala> val closureDiff = List(a.diff(b), b.diff(a), a.intersect(b))
closureDiff: List[List[(Int, Int)]] = List(List((2,3)), List((3,4)), List((1,2)))
Nonos
  • 2,450
  • 2
  • 23
  • 34
0

There is no function in the standard library that doe exactly what you need. However, an un-optimized version can be implemented easily in this manner(sorry for "span" mistake at first try).

def diffffK,V:(Map[K,V],Map[K,V],Map[K,V]) = {

val (both,left) = m1.partition({case (k,v) => m2.get(k)  == Some(v) })
val right = m2.filter({case (k,v) => both.get(k)  != Some(v) })
(both,left,right)

}

also, a map can be converted to a set with a single operator(toSet) and then you can use intercept, union and diff operators of Set.

Azzie
  • 366
  • 1
  • 4
  • the span isn't working since it only works at a single point, so once it hits a different value kv pair it lumps all the following as divergent kv pairs even if they are not. I tried toying around with partial and that might be useful (it's separating different kv pairs from the rest), but I've not yet figured out how to get it to match the Clojure function. But this is helping me make progress in that direction. – RatavaWen May 09 '14 at 19:42