在Groovy闭包内更新“it”

时间:2011-01-16 23:29:53

标签: grails groovy closures

我有一个域类,它只是一个字符串列表(youtubeLinks)。

保存这些链接时,我想删除视频ID并保存,而不是在UI端输入的整个网址。

这就是我正在尝试的(忽略正则表达式存在缺陷)

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

当我保存时,它会保存“it”的原始值。有没有办法更新此引用并正确保存?

感谢。

2 个答案:

答案 0 :(得分:22)

Groovy的each循环只是一个迭代器,因此它既不会影响它运行的集合,也不会返回它自己的值。它基本上等同于Java的“高级for循环(for-each)”,只是方便了动态类型和隐式循环变量(it)。虽然可以修改it,但它只是一个徒劳的企业,因为您只是更改对原始值的引用,而不是值本身。有关详情,请参阅this question

当您需要以某种方式修改集合中的每个元素时,惯用的Groovy(Grails)解决方案是使用collect方法。 Collect通过你提供的闭包转换每个元素,最终返回一个新的集合(因此,它实际上并没有“修改”任何东西)。

基本上,你可能想要做这样的事情:

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"]

..虽然实际上有很多方法可以在Groovy中完成这样的任务。

此外,Ted Naleid's blog有一些很好的Groovy模式匹配示例,您可能会发现它们很有用。

修改 您可以通过以下几种方式缩写您提交的解决方案:

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

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

或者这(虽然它有点人为)

youtubeLinks = youtubeLinks.join('\n').replaceAll(/.*\?v=/, '').split()

答案 1 :(得分:2)

你是对的,它最终就像是

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

谢谢,Northover。