Showing posts with label Map. Show all posts
Showing posts with label Map. Show all posts

Wednesday, March 10, 2010

How to reverse a map

Suppose you wish to take a map and swap the keys with values. The stackoverflow question Elegant way to revers a map in scala offers some good suggestions

  1. scala> val nodupes = Map(1 -> "a", 2-> "b", 3 -> "c")
  2. nodupes: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,a), (2,b), (3,c))
  3. // Scala 2.8+
  4. scala> nodupes map {_.swap}                          
  5. res4: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2), (c,3))
  6. // Scala 2.7
  7. scala> Map() ++ (nodupes map {case (k,v) => (v,k)})  
  8. res5: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2), (c,3))
  9. // watch out if the values have duplicates you will loose information:
  10. scala> val dupes = Map(1 -> "a", 2-> "b", 3 -> "b")  
  11. dupes: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,a), (2,b), (3,b))
  12. scala> dupes map {_.swap}                          
  13. res6: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,3))
  14. // a way to not loose any data
  15. scala> dupes groupBy {_._2} map {case (key,value) => (key, value.unzip._1)}     
  16. res12: scala.collection.Map[java.lang.String,scala.collection.immutable.Iterable[Int]] = Map((a,List(1)), (b,List(2, 3)))
  17. // I wanted to do the following for performance:
  18. scala> dupes.view groupBy {_._2} map {case (key,value) => (key, value.unzip._1)}
  19. java.lang.UnsupportedOperationException: IterableView((1,a), (2,b), (3,b)).newBuilder
  20. at scala.collection.TraversableViewLike$class.newBuilder(TraversableViewLike.scala:40)
  21. at scala.collection.IterableLike$$anon$1.newBuilder(IterableLike.scala:363)
  22. at scala.collection.TraversableLike$$anonfun$groupBy$1.apply(TraversableLike.scala:370)
  23. // but as you can see a view cannot yet be grouped.  Perhaps in the future.

Monday, November 9, 2009

Using objects as functions

A fun but easily overlooked fact is that many classes inherit from a Function class and therefore can be used where "normal" functions can be used.
  1. scala> List(1,2) map (Map(1->"one",2->"two"))
  2. res0: List[java.lang.String] = List(one, two)
  3. scala> List(3,3,2) map (List('a''b''c','d'))
  4. res2: List[Char] = List(d, d, c)

Friday, August 28, 2009

Apply-update methods

The apply and update methods have special meaning in Scala. They allow a developer to define semantics like java array access for an arbitrary class. For example:
  1. scala>class Phonebook {
  2.      |  val numbers = scala.collection.mutable.Map[String,String]()
  3.      | def apply(name:String):String = numbers(name)
  4.      | def update(name:String, number:String) = numbers(name) = number
  5.      | }
  6. defined class Phonebook
  7. scala>val book = new Phonebook() 
  8. book: Phonebook = Phonebook@1e406b09
  9. scala> book("jesse") = "123 456 7890"
  10. scala> println (book("jesse"))
  11. 123 456 7890

As you can see you can invoke the apply method in a similar way that [] are used on arrays in Java. When you call 'obj(param)' the call is translated by the compiler to: 'obj.apply(param)'. As such apply can have any number of arguments.

Similarly 'obj(param) = value' is compiled to 'obj.update(param,value)'. Again the update method can have as many arguments as you wish. However only the last argument is treated as a value. So:
  1. scala>class Echo {
  2.      | def update(n:String, n2:String, n3:String ) = println(n,n2,n3)
  3.      | }
  4. defined class Echo
  5. scala>val e = new Echo()
  6. e: Echo = Echo@2fa847df
  7. scala> e("hello", "hi") = "bonjour"
  8. (hello,hi,bonjour)
  9. scala> e("hello") = ("salut","bonjour")
  10. :7: error: wrong number of arguments for method update: (String,String,String)Unit
  11.        e("hello") = ("salut","bonjour")
  12.                   ^

This makes sense because if apply has many arguments representing the key then the same key must work for the update method for assignment. If you have many values to assign try:
  1. scala>class Phonebook {
  2.      |  val numbers = scala.collection.mutable.Map[String, (Int, Int)]()
  3.      | def apply(name:String) = numbers(name)
  4.      | def update(name:String, number:(Int,Int)) = numbers(name) = number
  5.      | }
  6. defined class Phonebook
  7. scala>val book2 = new Phonebook()
  8. book2: Phonebook = Phonebook@7a120cb3
  9. scala> book2("jesse") = (123, 4567890)
  10. scala>val (areaCode, local) = book2("jesse")
  11. areaCode: Int = 123
  12. local: Int = 4567890