Showing posts with label scala. Show all posts
Showing posts with label scala. Show all posts

Thursday, January 26, 2012

Yet another small Scala collection trick

Once again, my colleague Danny popped up a Java collection question. Given a data map with small lists as values, what is the shortest or best way to create a new, big list containing all individual elements from the small lists?

Best thing that we found in a few minutes, using car manufacturers and models as example data:

// setup
Map < String, List > carModels =
        new HashMap < String, List < String > > ();
carModels.put("Mazda",  Arrays.asList("MX-5", "RX-8"));
carModels.put("Audi",  Arrays.asList("A1", "A4", "A8"));

// actual work in 4 lines
List < String > allModels = new LinkedList < String > ();
for (List < String > brandModels: carModels.values()) {
        allModels.addAll(brandModels);
}

Now in Scala, back home ..

I took me a little research online, because I never used Scala on a daily basis, and miss a lot of knowledge on the collections API. Luckily it's not too hard to find good pointers online, and the Scala interpreter offers code completion help.

// setup
val carModels = Map("Mazda" -> List("MX-5", "RX-8"),
        "Audi" -> List("A1", "A4", "A8"))

// actual work, one liner
val allModels = carModels.values.reduce(_ ++ _)

The end result is actually not terribly hard to understand, but let me break it into pieces before I forget it myself:

carModels.values  // easy, similar as in Java

Now we have a collection of lists, and need to put all individual model names into one big list. Explicit looping is one way, but not very elegant. Another way of operating on a list, is using the "reduce" function. In general, you simply need to supply it a function which acts on two arguments, producing a single element.

For example, sum all numbers:

def add(x: Int, y: Int): x + y  // define simple 'add' function
List(1, 3, 5).reduce(add)   // evaluates to 9

So, this could lead to the following construction (with ++ for joining):

carModels.values.reduce( (models1, models2) => models1 ++ models2)

And thanks to implicit naming using underscores, we can end with:

carModels.values.reduce( _ ++ _ ) 

I still have to figure out what the difference is between the "++" and ":::" operators to join lists, and have many (!) other things to learn on Scala.

Feedback or corrections are very welcome!