ChrLipp
ChrLipp

Reputation: 15668

Map with default value created in a closure

I want to store objects in a map (called result). The objects are created or updated from SQL rows. For each row I read I access the map as follows:

def result = [:]
sql.eachRow('SELECT something') { row->
{
    // check if the Entry is already existing
    def theEntry = result[row.KEY]
    if (theEntry == null) {
        // create the entry
        theEntry = new Entry(row.VALUE1, row.VALUE2)

        // put the entry in the result map
        result[row.KEY] = theEntry
    }

    // use the Entry (create or update the next hierarchie elements)
}

I want to minimize the code for checking and updating the map. How can this be done? I know the function map.get(key, defaultValue), but I will not use it, because it is to expensive to create an instance on each iteration even if I don't need it.

What I would like to have is a get function with a closure for providing the default value. In this case I would have lazy evaluation.

Update
The solution dmahapatro provided is exactly what I want. Following an example of the usage.

// simulate the result from the select
def select = [[a:1, b:2, c:3], [a:1, b:5, c:6], [a:2, b:2, c:4], [a:2, b:3, c:5]]

// a sample class for building an object hierarchie
class Master {
    int a
    List<Detail> subs = []
    String toString() { "Master(a:$a, subs:$subs)" }
}

// a sample class for building an object hierarchie
class Detail {
    int b
    int c
    String toString() { "Detail(b:$b, c:$c)" }
}

// the goal is to build  a tree from the SQL result with Master and Detail entries
// and store it in this map
def result = [:]

// iterate over the select, row is visible inside the closure
select.each { row ->
    // provide a wrapper with a default value in a closure and get the key
    // if it is not available then the closure is executed to create the object
    // and put it in the result map -> much compacter than in my question
    def theResult = result.withDefault { 
        new Master(a: row.a)
    }.get(row.a)

    // process the further columns
    theResult.subs.add new Detail(b: row.b, c: row.c )
}

// result should be [
// 1:Master(a:1, subs:[Detail(b:2, c:3), Detail(b:5, c:6)]),
// 2:Master(a:2, subs:[Detail(b:2, c:4), Detail(b:3, c:5)])]
println result

What I learned from this sample:

Upvotes: 3

Views: 2668

Answers (1)

dmahapatro
dmahapatro

Reputation: 50245

You asked for it, Groovy has it for you. :)

def map = [:]

def decoratedMap = map.withDefault{
    new Entry()
}

It works the same way you would expect it to work lazily. Have a look at withDefault API for a detailed explanation.

Upvotes: 11

Related Questions