Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating "it" inside a Groovy closure

I have a domain class that is just a list of strings (youtubeLinks).

When saving these links I want to strip out the video ID and save it instead of the entire URL entered on the UI side.

This is what I'm trying (ignore that the regex is flawed)

youtubeLinks.each {
 def youtubeRegex = /v=(.*)/
 def matcher = ( it =~ youtubeRegex )
 it = matcher[0][1]
}

When I save this, it saves the original value of "it". Is there a way to update this reference and have it save properly?

Thanks.

like image 836
user577905 Avatar asked Jan 16 '11 23:01

user577905


People also ask

What is -> in Groovy?

It is used to separate where you declare bindings for your closure from the actual code, eg: def myClosure = { x, y -> x + y } the part before -> declares that the closure has two arguments named x and y while the second part is the code of the closure.

What is this in Groovy?

" this " in a block mean in Groovy always (be it a normal Java-like block or a Closure) the surrounding class (instance). " owner " is a property of the Closure and points to the embedding object, which is either a class (instance), and then then same as " this ", or another Closure.

What is a closure in gradle?

A closure is a short anonymous block of code. It just normally spans a few lines of code. A method can even take the block of code as a parameter. They are anonymous in nature. Following is an example of a simple closure and what it looks like.


2 Answers

Groovy's each loop is merely an iterator, and as such it neither affects the collection on which it operates, nor returns a value of its own. It's basically equivalent to Java's "advanced for loop (for-each)," only with the convenience of dynamic typing and an implicit loop variable (it). While it can be modified, it's a futile enterprise, as you'd be simply changing a reference to the original value, not the value itself. See this question for more on that.

When you need to modify every element within a collection somehow, the idiomatic Groovy (Grails) solution is to use the collect method. Collect transforms each element via the closure you provide, ultimately returning a new collection ( so, it doesn't actually "modify" anything).

Basically, you'll probably want to do something like this:

def links = '''http://www.youtube.com/watch?v=fl6s1x9j4QQ
http://www.youtube.com/watch?v=tCvMKcNJCAY
'''

assert (links =~ /watch\?v=(.*)/).collect{match -> match[1]} == ["fl6s1x9j4QQ", "tCvMKcNJCAY"]

..Though there are actually a number of ways one could go about such a task in Groovy.

Additionally, Ted Naleid's blog has some nice examples of Groovy pattern matching that you may find useful.

Edit Here are several ways in which you could abbreviate the solution you submitted:

youtubeLinks = youtubeLinks.collect{link -> (link =~ /\?v=(.*)$/)[0][1]}

or

youtubeLinks = youtubeLinks.collect{link -> link.replaceAll(/^.*\?v=/, "") }

or this (Though it's a little contrived)

youtubeLinks = youtubeLinks.join('\n').replaceAll(/.*\?v=/, '').split()
like image 81
Northover Avatar answered Oct 26 '22 09:10

Northover


You were right, it ended up being like

youtubeLinks = youtubeLinks.collect {
    def youtubeRegex = /v=(.*)[&]/
    def matcher = ( it =~ youtubeRegex )
    return matcher[0][1]
}

Thanks, Northover.

like image 27
user577905 Avatar answered Oct 26 '22 07:10

user577905