Friday, December 05, 2008

Easy Groovy Iteration

The problem

Let's say we want to check the correct mimetype and file extension of the file being uploaded. In a typical Java way I would write something like this:

boolean isRightType = "image/jpeg".equals(contentType)
|| "image/png".equals(contentType)
|| "image/x-png".equals(contentType);

boolean isRightFileExt = filename.endsWith(".png")
|| filename.endsWith(".jpg")
|| filename.endsWith(".jpeg");

Groovy solution

The Groovy way is much simpler and more elegant.

private static CONTENT_TYPES = ["image/jpeg", "image/png", "image/x-png"]
private static FILE_EXTENTIONS = [".png", ".jpg", ".jpeg"]
...
boolean isRightType = CONTENT_TYPES.any { it.equals(contentType) }
boolean isRightFileExt = FILE_EXTENTIONS.any { filename.endsWith(it) }

This was just a small example. What I really find useful are some of the following constructs that make my programming life really pleasant

Groovy Iteration

each

Simple collection iteration

def fibList = [1, 1, 2, 3, 5, 8, 13]
fibList.each { println it } // prints all of the numbers in the list

any

If you need to find out if any element of the collection meets the condition.

def fibList = [1, 1, 2, 3, 5, 8, 13]
assert fibList.any { it == 3 }
assert fibList.any { it - 2 > 10 }

This is pretty much equivalent to a Java construct

boolean any(List list, Condition cond)
for (E e : list) {
if (cond.meets(e)) {
return true;
}
}
return false;
}

every

If you need to find out if any element of the collection meets the condition.

def fibList = [1, 1, 2, 3, 5, 8, 13]
assert fibList.every { it > 0 }

This is pretty much equivalent to a Java construct

boolean every(List list, Condition cond)
for (E e : list) {
if (!cond.meets(e)) {
return false;
}
}
return true;
}

collect

If you need to create a new collection that contains each element of the original collection transformed in some way.

def fibList = [1, 1, 2, 3, 5, 8, 13]
assert fibList.collect { it - 1 } == [0, 0, 1, 2, 4, 7, 12]

This is pretty much equivalent to a Java construct

List every(List list, Command command)
List result = new ArrayList(list.size());
for (E e : list) {
result.add(command(e));
}
return result;
}

findAll

If you need to create a new collection that contains all elements that meet the condition.

def fibList = [1, 1, 2, 3, 5, 8, 13]
assert fibList.findAll { it > 1 && it < 5 } == [2, 3]

This is pretty much equivalent to a Java construct

List findAll(List list, Condition cond)
List result = new ArrayList(list.size());
for (E e : list) {
if (!cond.meets(e)) {
result.add(e);
}
}
return result;
}

find

If you need to find first element that matches the condition.

def fibList = [1, 1, 2, 3, 5, 8, 13]
assert fibList.find { it > 1 } == 2

This is pretty much equivalent to a Java construct

E findAll(List list, Condition cond)
for (E e : list) {
if (!cond.meets(e)) {
return e;
}
}
return null;
}

References

3 comments:

Anonymous said...

Wouldn't the first example you gave allow for a file to be uploaded as a JPG but with a PNG MIME type? I'd think you'd want to do a map of the mime type and extensions, then check both key/value data against the uploaded file info.

Unknown said...

Michael, you are right. But this is an example of how to do an iteration in Groovy, not a perfect solution to file uploads.

Alex Wei said...

Great write-up!
However, if you're stuck in the Java land, you can still use Commons Collections to achieve this, albeit a bit verbose:

* CollectionUtils.forAllDo = Groovy's each
* CollectionUtils.exists = Groovy's any
* CollectionUtils.countMatches can be used to achieve Groovy's every
* CollectionUtils.collect = Groovy's collect
* CollectionUtils.select = Groovy's findAll
* CollectionUtils.find = Groovy's find

Common Collections also provides many commonly used predicates to reduce your pain to write an anonymous class...

You may be interested in Google Collections as well, which has generics support.

But of course, the Java versions are always clumsy to use. I also prefer Groovy's syntax and its use of verbs, which are pretty much aligned with other languages...


Creative Commons License This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License.