1

.toMap shuffles my Items around / changes the order they were in, this messes with other parts of my code.

For example:

val seq = Seq("Test" -> DBType.Int, "Test2" -> DBType.Int, "Test3" -> DBType.Int);

val seqMap = seq.toMap;

println(seqMap)

The Output of this will be as expected:

Map(Test -> Int, Test2 -> Int, Test3 -> Int)

However, if I add more elements like this it gets weird:

val seq = Seq("Test" -> DBType.Int, "Test2" -> DBType.Int, "Test3" -> DBType.Int, "Test4" -> DBType.Int, "Test5" -> DBType.Int, "Test6" -> DBType.Int);
val seqMap = seq.toMap;

println(seqMap);

This will, and I really wanna know why, output this (the order has changed):

HashMap(Test3 -> Int, Test6 -> Int, Test5 -> Int, Test -> Int, Test4 -> Int, Test2 -> Int)

What the heck ? Did I not format it correctly ? Did I miss a comma ? I am really at the end of my expertise here.

I tried debugging it for almost an hour and a half but could not fix it. My idea would be that the .toMap function reshuffles the entire thing due to it being a HashMap now rather than a map, maybe the code in the background functions differently.

I am expecting my Seq of Tuples to be matches in order by the Map, as I need the keys to be in a specific (given) order.

I really hope someone can enlighten my why it behaves like this. I also did some google-ing but it just said: Use a Seq ! Well - duh - I am.

If you guys need more information on my code / problem feel free to comment.

EDIT: I am trying to join two tables together. One has a particular order of attributes and the other as well. My task is to join them in a fashion that the attributes of table 2 are being appended to those from table one. This however I have already managed. My problem is that the constructor of the schema class which is required to create a table wants to put these attributes in a map with the type they hold, f.e.: ("grade" -> Double)

So my order matters in that sens that the tests I am trying to full fill require me to have a specific order.

SmileyR6S
  • 15
  • 6
  • don't worry about the DBType.Int, it is just a way of specifying which type is used in a column of the application – SmileyR6S Jun 11 '23 at 16:13
  • 3
    A Map (the default ones at least) don't have any order guaranteed. Use another data structure Implementation like SortedMap for instance if you really need an order. – Gaël J Jun 11 '23 at 16:18
  • 4
    Does this answer your question? [Scala map sorting](https://stackoverflow.com/questions/4793490/scala-map-sorting) – Gaël J Jun 11 '23 at 16:19
  • @GaëlJ I feared that. I actually cant use another structure as this is a requirement in my given assignment. The constructor of the DB schema uses the .toMap function and it drives me nuts. I knew it deep inside me but I still need it to work with .toMap :/ Sad but true. I'd have done it in a completely different way as well but you know ... damn requirements. – SmileyR6S Jun 11 '23 at 16:21
  • 2
    `Maps` are unordered collections by nature, they may be sorted but never ordered. And any algorithm that uses them should not care about that. If you care, then you are using the wrong data structure or your code is wrong. - Now, it would be easier if you tell us what problem are you trying to solve and why the order matters. – Luis Miguel Mejía Suárez Jun 11 '23 at 16:22
  • Actually it does not, as the data I am given can not be sorted easily. I am joining two tables together with a given order. I cant order them by any algorithm. @GaëlJ – SmileyR6S Jun 11 '23 at 16:23
  • @LuisMiguelMejíaSuárez I added a comment to my original post. – SmileyR6S Jun 11 '23 at 16:27
  • 2
    Not sure why the order of columns matter at all, you access values in a table by the column name not the column order. – Luis Miguel Mejía Suárez Jun 11 '23 at 18:05
  • @LuisMiguelMejíaSuárez yeah that’s what I think as well. I’m actually very close to just rewrote this code to be something a lot more accessible. I think the whole idea of Maps and order contradicts itself, doesn’t it ? – SmileyR6S Jun 11 '23 at 18:28
  • 3
    @SmileyR6S ListMap (immutable) or LinkedHashMap (mutable) might work for you, since they'll maintain ordering. – resueman Jun 11 '23 at 19:52
  • @resueman will try and give feedback tomorrow. – SmileyR6S Jun 11 '23 at 20:28
  • @resueman thanks for the idea, I got it working ! I'd wanna give you credit so you might respond to this with a copy paste so I can mark it as answered ? Really appreciated it :D – SmileyR6S Jun 11 '23 at 22:22

1 Answers1

1

By default, Maps and Sets in Scala do not give any guarantee with regards to the order in which items are returned when iterating over them one by one. The only base collection to do so is the Seq.

You can learn more about Maps in Scala here. The documentation has also a page describing several concrete implementations of the Map trait which can suit you:

ListMap was also mentioned in a comment and you can use it as in the following example.

import scala.collection.immutable.ListMap

val seq = Seq("Test" -> 1, "Test2" -> 2, "Test3" -> 3)

seq.to(ListMap).foreach(println)

/* prints:
 *
 * ```
 * (Test,1)
 * (Test2,2)
 * (Test3,3)
 * ```
 */

You can play around with this code here on Scastie.

Expanding on that, note however that certain operations scale badly for this type, as explained in the documentation:

Entries are stored internally in reversed insertion order, which means the newest key is at the head of the list. As such, methods such as head and tail are O(n), while last and init are O(1). Other operations, such as inserting or removing entries, are also O(n), which makes this collection suitable only for a small number of elements.

If apart from insertion order you might be able to rely on some form ordering (e.g. the lexicographical order of the keys), you can use a SortedMap as well.

If you are open to using a mutable data structure, Scala also has its own LinkedHashMap (another one suggested in a comment above):

This class implements mutable maps using a hashtable. The iterator and all traversal methods of this class visit elements in the order they were inserted.

Finally, you can also use Java's LinkedHashMap:

Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map. (A key k is reinserted into a map m if m.put(k, v) is invoked when m.containsKey(k) would return true immediately prior to the invocation.)

stefanobaghino
  • 11,253
  • 4
  • 35
  • 63