How to list subdirectories beneath a directory in Scala

This is an excerpt from the Scala Cookbook. This is Recipe 12.10, “How to list subdirectories beneath a directory in Scala.”

Problem

You want to generate a list of subdirectories in a given directory.

Solution

Use a combination of the Java File class and Scala collection methods:

import java.io.File

// assumes that dir is a directory known to exist
def getListOfSubDirectories(dir: File): List[String] =
    dir.listFiles
       .filter(_.isDirectory)
       .map(_.getName)
       .toList

This algorithm does the following:

  • Uses the listFiles method of the File class to list all the files in the given directory as an Array[File].
  • The filter method trims that list to contain only directories.
  • map calls getName on each file to return an array of directory names (instead of File instances).
  • toList converts that to a List[String].

Calling toList isn’t necessary, but if it isn’t used, the method should be declared to return Array[String].

This method can be used like this:

getListOfSubDirectories(new File("/Users/Al")).foreach(println)

As mentioned, this method returns a List[String]. If you’d rather return a List[File], write the method as follows, dropping the map method call:

dir.listFiles.filter(_.isDirectory).toList

Discussion

This problem provides a good way to demonstrate the differences between writing code in a functional style versus writing code in an imperative style.

When a developer first comes to Scala from Java, she might write a more Java-like (imperative) version of that method as follows:

def getListOfSubDirectories1(dir: File): List[String] = {
    val files = dir.listFiles
    val dirNames = collection.mutable.ArrayBuffer[String]()
    for (file <- files) {
        if (file.isDirectory) {
            dirNames += file.getName
        }
    }
    dirNames.toList
}

After getting more comfortable with Scala, she’d realize the code can be shortened. One simplification is that she can eliminate the need for the ArrayBuffer by using a for loop with a yield expression. Because the method should return a List[String], the for loop is made to yield file.getName, and the for loop result is assigned to the variable dirs. Finally, dirs is converted to a List in the last line of the method, and it’s returned from there:

def getListOfSubDirectories2(dir: File): List[String] = {
    val files = dir.listFiles
    val dirs = for {
        file <- files
        if file.isDirectory
    } yield file.getName
    dirs.toList
}

Although there’s nothing wrong with this code — indeed, some programmers prefer writing for-comprehensions to using map — at some point, as the developer gets more comfortable with the Scala collections and FP style, she’ll realize the intention of the code is to create a filtered list of files, and using the filter method on the collection to return only directories will come to mind. Also, when she sees a for/yield combination, she should think, “map method,” and in short order, she’ll be at the original solution.