Why is "taking first n elements from an unsorted Map" meaningless?

67 Views Asked by At

With reference to the tutorial:
https://hello-scala.com/240-collections-maps.html
Please locate:
"Note that the last example probably only makes sense for a sorted Map."
"the last example" in the above statement:

val sortedMap = Map(
    1 -> "a", 
    2 -> "b", 
    3 -> "c",
    4 -> "d"
)
sortedMap.take(2) // Map(1 -> a, 2 -> b)  

According to the above statement, Map.take(2) makes sense only when the Map is sorted.

val unSortedMap = Map(
    2 -> "b", 
    1 -> "a", 
    4 -> "d",
    3 -> "c"
)
unSortedMap.take(2) // Map(2 -> b, 1 -> a)  

Question 1: Doesn't unSortedMap.take(2) make sense?
Question 2: If the answer to Q1 is no, why?

2

There are 2 best solutions below

0
HTNW On BEST ANSWER

The comment is referring to the literal SortedMap class. In a SortedMap, the entries are sorted by their keys. A SortedMap initialized with your data will always have the order SortedMap(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "d"), because that's the only way for the keys to be sorted. For a plain Map, the order is not guaranteed. In particular, for SortedMap, take respects ==: if xs == ys then xs.take(n) == ys.take(n). But for Map, your example shows that's not true. Since a Map is "supposed" to be an unordered collection of mappings, you're not supposed to depend on its "order". Notice that if you extend your maps to 6 elements, their order completely changes, because of implementation details of the library. It only makes sense to use take on a generic Map if you truly don't care which mappings you get out. E.g. it would be acceptable if you were "splitting" the map so you could operate on its pieces in parallel, or if you're "chunking" it so you can serialize it, but you can't use take for "logical" operations, like adding a bunch of mappings to a Map and expecting them to come back out in the order you put them in.

1
Tim On

The order of the keys is an unsorted Map is irrelevant. Two maps are the same if they have the same elements whatever order they were added:

val mapA = Map(
  4 -> "d",
  3 -> "c",
  2 -> "b",
  1 -> "a",
)

val mapB = Map(
  1 -> "a",
  2 -> "b",
  3 -> "c",
  4 -> "d",
)

val same = mapA == mapB // true
val diff = mapA.take(2) == mapB.take(2) // false or possibly true

So you can't tell which elements will be returned by take.