Archive for April, 2008

Groovy vs. Google Collections: Round #2

Tuesday, April 8th, 2008

For Round #2 of our code challenge, Dan has responded to my initial provocation in spades with a efficient example of Google CollectionsMultiMap utilities. His model involves car makes and models, their associations, and the easiest way to travel between them. At the core of the problem is the fact that a make can have many models, and we want an easy collection that will have utilities to get the data from both ends. Touché, my good man, because groovy doesn’t have a utility that matches the MultiMap functionality.

In order to quickly emulate the same data structure, I was forced to push a bunch of single-element maps into a list! Yuck!

Setting up my collection
def cars = []
cars << ['Ford':'Taurus'] << ['Ford':'Focus'] << ['Ford':'Mustang']
cars << ['Chevrolet':'Malibu'] << ['Chevrolet':'Impala'] << ['Chevrolet':'Corvette']
cars << ['Dodge':'Charger'] << ['Dodge':'Avenger'] << ['Dodge':'Viper']

But at least I have something I can work with now, albeit not the optimal collection that MultiMap would have provided in this case.

The real logic
def printModels = { make, pair ->
if (pair.any{it.key == make})
println "$make makes ${(pair.values() as List)[0]}”
make
}
def printMake = { model, pair ->
if (pair.any{it.value == model})
println “$model is made by ${(pair.keySet() as List)[0]}”
model
}
cars.inject(’Ford’, printModels)
cars.inject(’Chevrolet’, printModels)
cars.inject(’Dodge’, printModels)
cars.inject(’Impala’, printMake)

All the work is done in the closures. If I had the MultiMap’s capabilities, I could have created a map that would store multiple values for each key (see Dan’s initial volley), and I could have just called get() to retrieve a set of those values. But my groovy closures are looking specifically for either the key or value in the list of single-element maps. The closures are meant to be used with the inject method of groovy collections. The first printModels closure expects the make to be injected into it, and then compares it to each map key to find whether the current pair should be printed out. The printMakes closure expects the model to be passed, comparing it to the pair values.

I was disappointed to find out that groovy’s map implementation had no inverse operation. In java, this is not possible, because a key can have only one value, and if you try to inverse, you might possibly have a key with many values, breaking java into tiny pieces. Because the MultiMap allows keys to have multiple values, there is no danger in inversing the positions of keys and values, which is very handy (again, see how Dan used it)

The calls to inject actually execute the closures that print out the strings. The first three print out the models for each make injected into the closure, and the last one injects a model and prints out its make.

So there are some pluses, some minuses to using groovy for this sort of problem. On one hand, my total lines of code are still under 20, which is less than Dan’s example. However, I’m using a Frankensteined collection (a list of single-element maps). Can we get the best of both worlds somehow?

You can add any java library to your groovy project, just like it was a java project.

So let’s add Google’s Collections package as see if we can use it with groovy to make our code nicer!

Groovy and Google’s collections hand-in-hand
import com.google.common.collect.*

def cars = Multimaps.newHashMultimap()
cars.put('Ford','Taurus')

Ok… let’s stop right there. The groovy programmer in me really wanted to add to the map using cars.’Ford’ = ‘Taurus’, but no dice. This isn’t a groovy map, it is a google map, so I’ve lost all my groovy functionality, which isn’t very groovy. And my script from this point on is going to look suspiciously like Dan’s java program. So what is the point of this experiment? Well, it lets me know that if I run into a situation where I’ll need the functionality of a MultiMap, I can easily import the google jar and whip out an inverse-able MultiMap collection. Only, I won’t be able to use all the sugary groovy syntax with it. It is still better to have the option.

In summary, I think the google java collections package is downright cool. Anything that makes programming simpler and easier is good for our business. Groovy is great, but if I were working in java today, I would love this package. Hell, I might even want to use it with groovy someday. Thanks to Dan Lewis for participating in my challenge and being a good sport.


Groovy vs. Google Collections: Round #1

Friday, April 4th, 2008

In my last post, Dan Lewis (my friend and former coworker) responded with some counter-code from Google’s collections package. Instead of attempting to snap back with some witty technical retort, I challenged Dan to a code-off. Groovy collections vs. Google collections (in Java). Here is round one, and I’m going first with an example of finding combinations of collections. Hopefully, you can soon find Dan’s response in Google collection code-style on his blog.

Create the lists
def boys = ['Paco', 'Sven', 'Roger', 'Emelio']
def girls = ['Julia', 'Prudence', 'Lucy']

Now we have two simple lists, one of boys and one of girls. So what if these are all dancers, and we’d like to know all the combinations of dancers there could be? With groovy, this is extremely easy.

Find the combinations of dancers
def combos = [boys, girls].combinations()

Could it be any easier? This creates a list of lists, each inner list containing a possible combination:

[["Paco", "Julia"], ["Sven", "Julia"], ["Roger", "Julia"], ["Emelio", "Julia"], ["Paco", "Prudence"], ["Sven", "Prudence"], ["Roger", "Prudence"], ["Emelio", "Prudence"], ["Paco", "Lucy"], ["Sven", "Lucy"], ["Roger", "Lucy"], ["Emelio", "Lucy"]]

Now it would be nice if i could split the list up in some way, maybe keying by male or female. Groovy’s groupBy collection method is an easy way to do this. In the closure you provide, you simple return the key that you want to group the collection by, and it will return a map with that key pointing to a list of values. In the following example, because I know the first element of each combination is the boy and the second is the girl, I just specify a key by element position.

Grouping the combinations
def groupedByBoys = combos.groupBy { it[0] }
def groupedByGirls = combos.groupBy { it[1] }

Which returns these two groupings:

Grouped by boys
["Emelio":[["Emelio", "Julia"], ["Emelio", "Prudence"], ["Emelio", "Lucy"]], “Roger”:[["Roger", "Julia"], ["Roger", "Prudence"], ["Roger", "Lucy"]], “Sven”:[["Sven", "Julia"], ["Sven", "Prudence"], ["Sven", "Lucy"]], “Paco”:[["Paco", "Julia"], ["Paco", "Prudence"], ["Paco", "Lucy"]]]
Grouped by girls
["Lucy":[["Paco", "Lucy"], ["Sven", "Lucy"], ["Roger", "Lucy"], ["Emelio", "Lucy"]], “Prudence”:[["Paco", "Prudence"], ["Sven", "Prudence"], ["Roger", "Prudence"], ["Emelio", "Prudence"]], “Julia”:[["Paco", "Julia"], ["Sven", "Julia"], ["Roger", "Julia"], ["Emelio", "Julia"]]]

Beat that, Google Collections!