OrlandoL
OrlandoL

Reputation: 948

How can a Groovy method return multiple times?

I'm trying to get the first K elements from a sorted map by the following piece of code:

//return top rank k elements
public static LinkedHashMap<String,Double> getTopRank(int i){
    //store top k elements
    LinkedHashMap<String, Double> result=new LinkedHashMap<>();
    int count=0;
    //use the static rankMap in the class
    rankMap.each {key,value->
        result.put(key, value);
        count++;
        if(count>=i){
            println "Time to return"
            return result;
        }
    }
    //in case the loop does not work
    return result;
}

What I expect is that when the result Map already has a size of i elements, the method will return, giving me a i-size sorted map. Note that rankMap stores the elements in a certain order I want, and its size is far bigger than int i I pass to the method. And I'm calling the method by

LinkedHashMap<String,Double> content=getTopRank(outputSize);

But unexpectedly finally the content had the size of rankMap rather than i! And in the console I saw hundreds of Time to return lines. The line return result was executed again and again until it reached the end of rankMap. I'm pretty sure that the line getTopRank(outputSize) was not in a loop. Then it seems strange to me why this method can return multiple times without ending. Is it caused by my putting return statement in the closure? Please advise or tell me how this is true in Groovy. One step further, how can I get only first k elements from a sorted map then?

Upvotes: 5

Views: 2701

Answers (3)

ScientificMethod
ScientificMethod

Reputation: 513

public static LinkedHashMap<String,Double> getTopRank(int i){
    rankMap.take(i)
}

http://www.groovy-lang.org/gdk.html
http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Map.html#take(int)

Upvotes: 0

Will
Will

Reputation: 14539

The method itself is not returning. each is a method which receives a closure. Closures have their own returning context, which is not tied to the method who invoked them, thus, the loop is not broken.

I'd like to suggest getting a range from the map's entrySet and collecting the resulting entries:

def getTopRank(int i) {
    rankMap
        .entrySet()
        .toList()[0..<i]
        .collectEntries()
}

rankMap = [
    'Beatles'        : 'The White Album',
    'Pink Floyd'     : 'The Dark Side of the Moon',
    'Rolling Stones' : 'Sticky Fingers',
    'The Doors'      : 'Morrison Hotel',
    'Bob Dylan'      : 'Bob Dylan'
]


assert getTopRank(2) == [
    'Beatles'    : 'The White Album',
    'Pink Floyd' : 'The Dark Side of the Moon']

assert getTopRank(4) == [
    'Beatles'        : 'The White Album',
    'Pink Floyd'     : 'The Dark Side of the Moon',
    'Rolling Stones' : 'Sticky Fingers',
    'The Doors'      : 'Morrison Hotel',]

Upvotes: 2

injecteer
injecteer

Reputation: 20709

You misunderstood key concepts of Groovy.

The only way to finish the each() execution before reaching the end, is to throw an exception. If your want to exit the loop conditionally, use standard loop types like for or while:

int count=0
def result = [:]
for( def e in rankMap ){
  count++
  result[ e.key ] = e.value
  if( i <= count ) return result
}

Upvotes: 4

Related Questions