Reverse ListMap

I wrote a small code to count number of words in a text:

val fileSource = Source.fromFile("alice29.txt")
val words: Array[String] = fileSource.mkString.split("\\W+").map(x => x.toLowerCase())
println("different words: " + words.length)
println(words.groupBy(identity).map(t => (t._1, t._2.length)))
import scala.collection.immutable.ListMap
val unordered : Seq[(String, Int)] = words.groupBy(identity).map(t => (t._1, t._2.length)).toSeq 

I would like to reverse the order by using reverse. But it seems reverse cannot be applied to the ListMap. Why? How to do it properly?

The error message is as follows

error: value reverse is not a member of scala.collection.immutable.ListMap[String,Int]

When you tried reverse, did you get an error message, unexpected behavior, or what? If it was an error message, please include the entire message.

Please see my edit

@yarchik You can usually find answers to these questions in the Standard Library. (Any particular reason why you use ListMap?)
Looking at Scala Standard Library 2.13.8 - scala.collection.immutable.ListMap we see that reverse is not available for ListMap.
It says

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.

So I can kind of understand why reverse was not implemented. ListMap seems to be intended for a very particular use case.

You can use reverse on the Seq you initially had.

1 Like

ListMap first and foremost is a Map, which doesn’t have a notion of element ordering, thus no #reverse. It does give some guarantees regarding the order of elements in iterators based on insertion order, but this is just an artifact of internal in-memory organization and not reflected in the API (and I wouldn’t really want to rely on these guarantees for anything important).

You could apply [EDIT: #sortBy() and] #reverse on the unordered pair seq, but you could sort in the required ordering directly, omitting the additional reversal.

unordered.sorted([(String, Int), Int](_._2).reverse)

Unrelated remarks:

  • You should always close the Source after use. (Consider using scala.util.Using.)
  • I’d try to move away from Array asap, e.g. by applying #toVector or similar after the #split.
  • You could replace #groupBy() with #groupMapReduce():
    val unordered = words.groupMapReduce(identity)(_ => 1)(_ + _).toVector
1 Like

@sangamon Very useful, I learn a lot from all the remarks.