How to write a conditional collect in groovy?

Imagine I have this structure:

class Foo {
   String bar
}

Now imagine I have several instance of Foo whose bar value is baz_1, baz_2, and zab_3.

I want to write a collect statement that only collects the bar values which contain the text baz. I cannot get it to work, but it would look something like this:

def barsOfAllFoos = Foo.getAll().bar
assert barsOfAllFoos == [ 'baz_1', 'baz_2', 'zab_3' ]
def barsWithBaz = barsOfAllFoos.collect{ if( it.contains( "baz" ) { it } ) } // What is the correct syntax for this?
assert barsWithBaz == [ 'baz_1', 'baz_2' ]

Solution 1:

You need findAll:

barsOfAllFoos.findAll { it.contains 'baz' }

Solution 2:

If you want to both filter and transform there's lots of ways to do this. After 1.8.1 I'd go with #findResults and a closure that returns null for the elements I want to skip.

def frob(final it) { "frobbed $it" }

final barsWithBaz = barsOfAllFoos.findResults {
    it.contains('baz')? frob(it) : null
}

In earlier versions you can use #findAll and #collect

final barsWithBaz = barsOfAllFoos
                  . findAll { it.contains('baz') }
                  . collect { frob(it) }

Or #sum

final barsWithBaz = barsOfAllFoos.sum([]) {
    it.contains('baz')? [frob(it)] : []
}

Or #inject

final barsWithBaz = barsOfAllFoos.inject([]) {
    l, it -> it.contains('baz')? l << frob(it) : l
}

Solution 3:

Using findResults did not work for me... If you want to collect a transformed version of the values matching the condition (for instance a regex search of many lines) you can use collect followed by find or findAll as follows.

def html = """
    <p>this is some example data</p>
    <script type='text/javascript'>
        form.action = 'http://www.example.com/'
        // ...
    </script>
"""

println("Getting url from html...")
// Extract the url needed to upload the form
def url = html.split("\n").collect{line->
    def m = line =~/.*form\.action = '(.+)'.*/
    if (m.matches()) {
        println "Match found!"
        return m[0][1]
    }
}.find()

println "url = '${url}'"

This returns the part of the line matching the given pattern.

Getting url from html...
Match found!
url = 'http://www.example.com/'