Search

Dark theme | Light theme

September 27, 2011

Groovy Goodness: Use inject Method on a Map

The inject() method is since Groovy 1.8.1 also available for Map objects. The closure arguments accepts two or three arguments. With the three-argument variant we get the key and value separately as arguments. Otherwise we get a map entry as closure argument.

// 3-argument closure with key, value.
def m = [user: 'mrhaki', likes: 'Groovy']
def sentence = m.inject('Message: ') { s, k, v ->
    s += "${k == 'likes' ? 'loves' : k} $v "
}

assert sentence.trim() == 'Message: user mrhaki loves Groovy'

// 2-argument closure with entry. 
def map = [sort: 'name', order: 'desc']
def equalSizeKeyValue = map.inject([]) { list, entry ->
    list << (entry.key.size() == entry.value.size())
}

assert equalSizeKeyValue == [true, false]

September 26, 2011

Groovy Goodness: Access ResultSetMetaData with Groovy SQL

Groovy's SQL support allows us to access ResultSetMetaData with a closure when we use the query methods rows() and eachRow(). We can pass a closure as the last argument of these methods. The closure parameter is the ResultSetMetaData object. The closure is only invoked once after the query is executed.

@Grapes([
    @Grab(group='com.h2database', module='h2', version='1.3.160'),
    @GrabConfig(systemClassLoader = true)
])
import com.h2database.*
import groovy.sql.*

def db = Sql.newInstance('jdbc:h2:mem:', 'sa', '', 'org.h2.Driver')

// Setup database.
db.execute '''
    create table if not exists languages(
        id int primary key,
        name varchar(20) not null
    )
'''

db.execute "insert into languages values(1, 'Groovy')"
db.execute "insert into languages values(2, 'Java')"

// Sample of meta closure:
String query = 'select id as identifier, name as langName from languages'
def rows = db.rows(query, { meta ->
    assert meta.tableName == 'languages'
    assert meta.columnCount == 2

    assert meta.getColumnLabel(1) == 'IDENTIFIER'
    assert meta.getColumnName(1) == 'ID'
    assert meta.getColumnTypeName(1) == 'INTEGER'

    assert meta.getColumnLabel(2) == 'LANGNAME'
    assert meta.getColumnName(2) == 'NAME'
    assert meta.getColumnTypeName(2) == 'VARCHAR'
})

September 23, 2011

Groovy Goodness: Using Named (Ordinal) Parameters with Groovy SQL

Groovy has great SQL support built-in. With just a few lines of code we can access a database and write SQL statements and execute them. To query data we can for example use the rows() method. We pass a SQL query and we get a List of GroovyResultSet objects back. But we can also use other methods like query() or eachRow().

The query may contain named placeholders. For example :name or ?.id. Both the : and ?. syntax are supported. To give the named parameters a value we pass an extra object to the query method. This object can be a Map, Expando object or any object with properties matching the names of the named parameter.

Besides we can even used named ordinal parameters. Then we must use the ?. syntax. We set the ordinal (1-based) after the question mark. Now we pass multiple objects with values for the named parameters to the query method. For each ordinal we must pass an object with values for that named ordinal parameter.

The following sample shows the possibilities:

@Grapes([
    @Grab(group='com.h2database', module='h2', version='1.3.160'),
    @GrabConfig(systemClassLoader = true)
])
import com.h2database.*
import groovy.sql.*

def db = Sql.newInstance('jdbc:h2:mem:', 'sa', '', 'org.h2.Driver')

// Setup database.
db.execute '''
    create table if not exists languages(
        id int primary key,
        name varchar(20) not null
    )
'''

db.execute "insert into languages values(1, 'Groovy')"
db.execute "insert into languages values(2, 'Java')"

// ------------------
// Ready for queries:
// ------------------

// Use :name syntax for named parameters.
def result = db.rows('select * from languages where name = :name',
                     [name: 'Groovy'])
assert result[0] == [ID: 1, NAME: 'Groovy']

// Different syntax with ?.name.
result = db.rows('select * from languages where name = ?.name',
                 [name: 'Groovy'])
assert result[0] == [ID: 1, NAME: 'Groovy']

// We can use our own classes with properties matching
// query parameters.
class QueryParams {
    String name = 'Java'
    Integer id = 2
}

result = db.rows('select * from languages where name = ?.name and id = :id',
                 new QueryParams())
assert result[0] == [ID: 2, NAME: 'Java']


// We can use ordinal named parameters.
// For each ordinal placeholder we must pass an object
// with values.
result = db.rows('select * from languages where name = ?1.name and id = ?2.id',
                 [name: 'Groovy'] /* ?1.name */, [id: 1] /* ?2.id */)
assert result[0] == [ID: 1, NAME: 'Groovy']

result = db.rows('select * from languages where name = ?1.name and id = ?2.id',
                 [name: 'Groovy'], new Expando([id: 1]))
assert result[0] == [ID: 1, NAME: 'Groovy']

result = db.rows('select * from languages where name = ?1.name or name = ?2.name',
                 new QueryParams(), [name: 'Groovy'])
assert result[0] == [ID: 1, NAME: 'Groovy']
assert result[1] == [ID: 2, NAME: 'Java']

September 22, 2011

Groovy Goodness: Use Connection Parameters to Get Text From URL

For a long time we can simply get the text from an URL in Groovy. Since Groovy 1.8.1 we can set parameters to the underlying URLConnection that is used to get the content. The parameters are passed as a Map to the getText() method or to the newReader() or newInputStream() methods for an URL.

We can set the following parameters:

  • connectTimeout in milliseconds
  • readTimeout in milliseconds
  • useCaches
  • allowUserInteraction
  • requestProperties is a Map with general request properties

// Contents of http://www.mrhaki.com/url.html:
// Simple test document
// for testing URL extensions
// in Groovy.

def url = "http://www.mrhaki.com/url.html".toURL()

// Simple Integer enhancement to make
// 10.seconds be 10 * 1000 ms.
Integer.metaClass.getSeconds = { ->
    delegate * 1000
}

// Get content of URL with parameters.
def content = url.getText(connectTimeout: 10.seconds, readTimeout: 10.seconds,
                          useCaches: true, allowUserInteraction: false,
                          requestProperties: ['User-Agent': 'Groovy Sample Script'])

assert content == '''\
Simple test document
for testing URL extensions
in Groovy.
'''

url.newReader(connectTimeout: 10.seconds, useCaches: true).withReader { reader ->
    assert reader.readLine() == 'Simple test document'
}

September 21, 2011

Groovy Goodness: Streaming JSON with StreamingJsonBuilder

Since Groovy 1.8 we can use JSONBuilder to create JSON data structures. With Groovy 1.8.1 we have a variant of JsonBuilder that will not create a data structure in memory, but will stream directly to a writer the JSON structure: StreamingJsonBuilder. This is useful in situations where we don't have to change the structure and need a memory efficient way to write JSON.

import groovy.json.*

def jsonWriter = new StringWriter()
def jsonBuilder = new StreamingJsonBuilder(jsonWriter)
jsonBuilder.message {
    header {
        from(author: 'mrhaki')  
        to 'Groovy Users', 'Java Users'
    }
    body "Check out Groovy's gr8 JSON support."
}
def json = jsonWriter.toString()
assert json == '{"message":{"header":{"from":{"author":"mrhaki"},"to":["Groovy Users","Java Users"]},"body":"Check out Groovy\'s gr8 JSON support."}}'

def prettyJson = JsonOutput.prettyPrint(json)
assert prettyJson == '''{
    "message": {
        "header": {
            "from": {
                "author": "mrhaki"
            },
            "to": [
                "Groovy Users",
                "Java Users"
            ]
        },
        "body": "Check out Groovy's gr8 JSON support."
    }
}'''


new StringWriter().withWriter { sw ->
    def builder = new StreamingJsonBuilder(sw)

    // Without root element.
    builder name: 'Groovy', supports: 'JSON'

    assert sw.toString() == '{"name":"Groovy","supports":"JSON"}'
}

new StringWriter().with { sw ->
    def builder = new StreamingJsonBuilder(sw)

    // Combine named parameters and closures.
    builder.user(name: 'mrhaki') {
        active true
    }
    
    assert sw.toString() == '{"user":{"name":"mrhaki","active":true}}'    
}

September 20, 2011

Groovy Goodness: Transform Items into a Collection with collectMany

In Groovy we can use the collectMany() method to transform items from a collection into a collection. The resulting collection is then flattened into a single collection. This means we can use the closure to return a collection with values and at the end all these collections are flattened into a single collection.

def text = 'groovy' as String[]

def mixedCase = text.collectMany { [it, it.toUpperCase()] }
assert mixedCase == ['g','G','r','R','o','O','o','O','v','V','y','Y']
assert text == 'groovy' as String[]


def list = [12, 20, 34]
def result = list.collectMany { [it, it*2, it*3] }

assert result == [12,24,36,20,40,60,34,68,102]
assert list == [12, 20, 34]

September 19, 2011

Groovy Goodness: Collect on Nested Collections

The collect() method has been around in Groovy for a long time and it is very useful. With the collect() method we can iterate through a collection and transform each element with a Closure to another value. To apply a transformation to collections in collections we can use the collectAll() method. Since Groovy 1.8.1 the collectAll() method is deprecated in favor of the new collectNested() method. So with collectNested() we can transform elements in a collection and even in nested collections and the result will be a collection (with nested collections) with transformed elements.
We can pass an initial collection to the method to which the transformed elements are added.

def list = [10, 20, [1, 2, [25, 50]], ['Groovy']]

assert list.collectNested { it * 2 } == [20, 40, [2, 4, [50, 100]], ['GroovyGroovy']]
assert list.collectNested(['1.8.1', [0]]) { it * 2 } == ['1.8.1', [0], 20, 40, [2, 4, [50, 100]], ['GroovyGroovy']]
assert list.collectNested([]) { it * 2 } == [20, 40, [2, 4, [50, 100]], ['GroovyGroovy']]

// Simple collect will duplicate the nested collection instead
// of elements in the nested collection.
assert list.collect { it * 2 } == [20, 40, [1, 2, [25, 50], 1, 2, [25, 50]], ['Groovy', 'Groovy']]

September 16, 2011

Groovy Goodness: GroupBy with Multiple Closures

We can group elements in a List or Map with the groupBy() method for a long time in Groovy. We pass a closure with the grouping condition to get a Map with the items grouped. And since Groovy 1.8.1 we can use more than closure to do the grouping. We can use it for both List and Map objects.

import static java.util.Calendar.*

class User {
    String name
    String city
    Date birthDate
    public String toString() { name }
}

def users = [
    new User(name:'mrhaki', city: 'Tilburg',   birthDate: Date.parse('yyyy-MM-dd', '1973-9-7')),
    new User(name:'bob',    city: 'New York',  birthDate: Date.parse('yyyy-MM-dd', '1963-3-30')),
    new User(name:'britt',  city: 'Amsterdam', birthDate: Date.parse('yyyy-MM-dd', '1980-5-12')),
    new User(name:'kim',    city: 'Amsterdam', birthDate: Date.parse('yyyy-MM-dd', '1983-3-30')),
    new User(name:'liam',   city: 'Tilburg',   birthDate: Date.parse('yyyy-MM-dd', '2009-3-6'))
]

def result = users.groupBy({it.city}, {it.birthDate.format('MMM')})

assert result.toMapString() == 
    '[Tilburg:[Sep:[mrhaki], Mar:[liam]], New York:[Mar:[bob]], Amsterdam:[May:[britt], Mar:[kim]]]'

assert result.Amsterdam.size() == 2
assert result.Tilburg.Mar.name == ['liam']

result = users.groupBy({it.name[0]}, {it.city})
assert result.toMapString() ==
    '[m:[Tilburg:[mrhaki]], b:[New York:[bob], Amsterdam:[britt]], k:[Amsterdam:[kim]], l:[Tilburg:[liam]]]'
assert result.k.Amsterdam.name == ['kim']  

// groupBy with multiple closues also works on Map
def usersByCityMap = users.groupBy({it.city})
def resultMap = usersByCityMap.groupBy({it.value.size()}, { k,v -> k.contains('i') })
assert resultMap.toMapString() ==
    '[2:[true:[Tilburg:[mrhaki, liam]], false:[Amsterdam:[britt, kim]]], 1:[false:[New York:[bob]]]]'
assert resultMap[1].size() == 1
assert resultMap[2].size() == 2
assert resultMap[2][true].Tilburg.name.join(',') == 'mrhaki,liam'

Test the code on Groovy web console.

September 15, 2011

Groovy Goodness: Sort or Remove Duplicates without Changing the Original Collection

We can sort and remove duplicates from collections or arrays in Groovy for a long time. The sort() and unique() methods alter the original collection, so there is a big side effect. Since Groovy 1.8.1 we can use a boolean argument to indicate if we want to mutate the original collection or not. So now we can remove the side effect and leave the original collection or array unchanged.

The reverse() method also accepts this boolean argument since Groovy 1.8.1, but the default behavior was already to leave the original collection unchanged. Now we can explicitly tell the reverse() method to change the original collection.

class User implements Comparable {
    String username, email
    boolean active
    
    int compareTo(Object other) {
        username <=> other.username
    }
    
    String toString() { "[User:$username,$email,$active]" }
}

def mrhaki1 = new User(username: 'mrhaki', email: 'mrhaki@localhost', active: false)
def mrhaki2 = new User(username: 'mrhaki', email: 'user@localhost', active: true)

def hubert1 = new User(username: 'hubert', email: 'user@localhost', active: false)
def hubert2 = new User(username: 'hubert', email: 'hubert@localhost', active: true)

def users = [mrhaki1, mrhaki2, hubert1, hubert2]


/* Sort */
def sortedUsers = users.sort(mutate = false)  // Don't mutate original List
assert sortedUsers == [hubert1, hubert2, mrhaki1, mrhaki2]
assert users == [mrhaki1, mrhaki2, hubert1, hubert2]

// Default behaviour is to change original collection
sortedUsers = users.sort() 
assert sortedUsers == [hubert1, hubert2, mrhaki1, mrhaki2]
assert users == [hubert1, hubert2, mrhaki1, mrhaki2]


/* Unique */
def uniqueUsers = users.unique(mutate = false)  // Don't mutate original List
assert uniqueUsers == [hubert1, mrhaki1]
assert users == [hubert1, hubert2, mrhaki1, mrhaki2]

// Default behaviour is to change original collection
uniqueUsers = users.unique()   
assert uniqueUsers == [hubert1, mrhaki1]
assert users == [hubert1, mrhaki1]


/* Reverse */
def reverseUsers = users.reverse()  // mutate false is default
assert reverseUsers == [mrhaki1, hubert1]
assert users == [hubert1, mrhaki1]

// Default behaviour is to leave original collection alone
reverseUsers = users.reverse(mutate = true)  // Mutate original list
assert reverseUsers == [mrhaki1, hubert1]
assert users == [mrhaki1, hubert1]

You can try this code in Groovy web console.

September 14, 2011

Groovy Goodness: Take and Drop Items from a List

When working with List object we get a lot of nice and useful methods we can use in Groovy. Since Groovy 1.8.1 we can use the methods take() and drop(). With the take() method we get items from the beginning of the List. We pass the number of items we want as an argument to the method.

To remove items from the beginning of the List we can use the drop() method. Here we pass the number of items we want to drop as an argument to the method. Please keep in mind the original list is not changed, the result of the drop() method is a new list.

def list = ['Simple', 'list', 'with', 5, 'items']

assert list.take(1) == ['Simple']
assert list.take(2) == ['Simple', 'list']
assert list.take(0) == []
// Whole list, because we take more items then the size of list
assert list.take(6) == ['Simple', 'list', 'with', 5, 'items']

assert list.drop(1) == ['list', 'with', 5, 'items']
assert list.drop(3) == [5, 'items']
assert list.drop(5) == []
assert list.drop(0) == ['Simple', 'list', 'with', 5, 'items']
assert list == ['Simple', 'list', 'with', 5, 'items']

// After reading Tim Yates' comment I have added 
// more samples showing drop() and take() also work on
// Maps, Iterators, CharSequences and arrays.
def array = ['Rock on!', 'Groovy baby!'] as String[]
assert array.take(1) == ['Rock on!'] as String[]
assert array.drop(1) == ['Groovy baby!'] as String[]

def range = 0..10
assert range.take(2) == [0,1]
assert range.take(4) == 0..3
assert range.drop(5) == 5..10

def map = [1: 'one', 2: 'two', 3: 'three']
assert map.take(2) == [1: 'one', 2: 'two']
assert map.drop(2) == [3: 'three']
assert map.drop(3) == [:]

def s = 'Hello Groovy world!'
assert s.take(5) == 'Hello'
assert s.drop(6) == 'Groovy world!'

September 13, 2011

Groovy Goodness: Find Elements Matching Groovy Truth

In Groovy 1.8.1 we can use the find() and findAll() methods to find the elements in for example a List wich complies to Groovy truth. The find() and findAll() methods are defined in the Groovy GDK on the Object class, so we can use it for every object that can be iterated.

class User {
    boolean enabled
    
    Boolean asBoolean() {
        enabled
    }
}

def items = [0, false, null, [], [:], '', new User(enabled: false), 'Groovy rocks!', 101]

assert items.find() == 'Groovy rocks!'

assert items.findAll() == ['Groovy rocks!', 101]

September 12, 2011

Groovy Goodness: Transform Collection to a Map with collectEntries

Since Groovy 1.7.9 we can use the collectEntries method for a Collection to get a Map. We use a closure to transform the elements in the collection to a map entry. And the end result is a Map with all the map entries.

def words = ['Groovy', 'Rocks', 'Big', 'Time']

def result = words.collectEntries { 
    [(it): it.contains('o')]
}

assert result.Groovy && result.Rocks
assert !result.Big && !result.Time