Is the take function well defined on a Scala Map?

147 Views Asked by At

Is the take function safe to use on a Scala Map? I thought that Maps were unordered, so that myMap.take(2) would return 2 random elements of myMap. But testing on the interpreter makes me feel that it is safe:

scala> val z = Map(1 -> 10, 2 -> 20, 3 -> 30, 4 -> 40)
z: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2 -> 20, 3 -> 30, 4 -> 40)

scala> z.take(2)
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2 -> 20)

scala> z.take(2)
res2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2 -> 20)

scala> z.take(2)
res3: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2 -> 20)

scala> z.take(2)
res4: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2 -> 20)

So, is it safe to use take on a Map, or do I need to use a ListMap?

4

There are 4 best solutions below

0
On BEST ANSWER

You'll very likely get the same 2 every time you take(2), but there's no telling which 2 you'll get in the first place.

val y = Map(1 -> 10, 2 -> 20, 3 -> 30, 4 -> 40, 5 -> 5)
y.take(2)  //res0: Map[Int,Int] = Map(5 -> 5, 1 -> 10)
0
On

Without knowing exactly what you're trying to achieve, you might want to consider using a SortedMap, which guarantees that keys are sorted in order. Consider the following two examples in the Scala REPL:

scala> val z = Map(4 -> 40, 3 -> 30, 1 -> 10, 2 -> 20)
z: scala.collection.immutable.Map[Int,Int] = Map(4 -> 40, 3 -> 30, 1 -> 10, 2 -> 20)

scala> z.take(2)
res0: scala.collection.immutable.Map[Int,Int] = Map(4 -> 40, 3 -> 30)

scala> import scala.collection.immutable.SortedMap
import scala.collection.immutable.SortedMap

scala> val zs = SortedMap(4 -> 40, 3 -> 30, 1 -> 10, 2 -> 20)
zs: scala.collection.immutable.SortedMap[Int,Int] = Map(1 -> 10, 2 -> 20, 3 -> 30, 4 -> 40)

scala> zs.take(2)
res1: scala.collection.immutable.SortedMap[Int,Int] = Map(1 -> 10, 2 -> 20)

As @jwvh pointed out, in a regular Map there's no defined ordering, but that ordering is fixed once defined, so you're always going to get the same members returned for .take(2) given the same map instance. However, which values you get returned will depend upon the order in which the map was created.

Meanwhile, in a SortedMap, the keys are always ordered, so that when you employ .take(2) elements, you will always get the same two elements, regardless of the definition order: those that have the two lowest key values.

Refer to the help on the take method for further information.

2
On

Immutable maps in Scala have special subtypes for 0 to 4 elements, which you can see in the code, as an optimization. As it happens, Map.apply with up to 4 arguments produces an instance of one of these classes, so iterating it will always produce elements in the order passed to Map.apply.

0
On

Is the take function well defined on a Scala Map?

This depends on your exact definition of "well-defined", but I would argue that, yes, for most common interpretations of "well-defined", it is well-defined.

The documentation lists all possible outcomes for all possible inputs clearly, and there are no ambiguous or unspecified inputs. All corner cases are covered:

  • If the collection has less than n elements, the whole collection will be returned.
  • If n is negative, an empty collection will be returned.
  • If the collection is un-ordered, arbitrary elements will be returned.

Is the take function safe to use on a Scala Map?

This depends on your exact definition of "safe", but I would argue that, yes, for most common interpretations of "safe", it is safe.

  • It is statically type-safe.
  • It is dynamically type-safe.
  • It does not throw an exception.

I thought that Maps were unordered, so that myMap.take(2) would return 2 random elements of myMap.

No. The documentation says:

Note: might return different results for different runs, unless the underlying collection type is ordered.

This means that arbitrary elements will be returned, not random ones.