Updating "it" inside a Groovy closure - grails

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.

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()

You were right, it ended up being like
youtubeLinks = youtubeLinks.collect {
def youtubeRegex = /v=(.*)[&]/
def matcher = ( it =~ youtubeRegex )
return matcher[0][1]
}
Thanks, Northover.

Related

Redefining TCL foreach

I would like to redefine TCL's foreach, since multiple applications of foreach break some code.
Detailed problem description
The actual case where the code breaks happens in Vivado, a program using TCL for scripting. They supply an API that specifies get_<someObject> methods, which returns a list of objects (actually something that behaves like a list, but is implemented by xilinx to handle caching/printing a bit differently). To handle these lists I use foreach. The first time I use foreach on the list, I get <objects> in the loop variable.
However, if I apply foreach on a single object, the object is converted into a string representing its name. The problem is, that the API function get_property expects an object.
While applying foreach twice, does not seem to make sense, this might happen if you write two functions that take a list of objects and operate on them.
i.e.
proc a {obj} {
puts "logging [llength $obj] objects:"
foreach o $obj {
puts "$o has column index [get_property COLUMN_INDEX $o]"
}
}
proc b {obj} {
foreach o $obj {
a $o
puts "working on $o"
get_property COLUMN_INDEX $o
}
}
If we now call these functions as follows
a [get_clock_regions X0Y0] # ok (the list is turned into single objects in foreach)
b [get_clock_regions X0Y0] # crashes inside a (the list from get_clock_regions is
# turned into objects by the foreach in b
# a is then called with a single object,
# the foreach now turns the object into a string
# representing the name, but get_property
# does not work on strings => error
I asked if the behaviour can be fixed in this question on the Xilinx Forums, however I am looking for a workaround for the meantime.
What I am trying to do
I would like to implement a workaround for the meantime. While I can do
a [list $o]
inside b, I think it would be nicer if I can redefine foreach to not break the above case. This way, I can simply drop my redefinition of foreach if Xilinx can fix the behaviour. (I am expecting foreach to work the same regardless if I have a list with one element or a single element, to my understanding this is the same in TCL).
My issues with redefining foreach are:
How do I know if foreach is called as
foreach {varA varB} {valueList} {...}
foreach {varA varB} {valueList1 valueList2} {...}
Is there a way to test if I have an object or a list containing one object? The Idea is two detect if it is just an object, if so wrap it into a list, which then can be unwrapped back to object by the normal foreach, however I have no clue how to detect this case.
Outline code I would like to write:
proc safeForeach {varnames valueLists body} {
if { thereAreMultiple valueLists } { # this issue no 1
foreach valueList $valueLists {
if {wouldDecayToString $valueLists} { # this is issue no 2
set valueLists [list $valueLists]
}
}
} else {
if {wouldDecayToString $valueLists} { # this is issue no 2 again
set valueLists [list $valueLists]
}
}
#the next line should be wraped in `uplevel`
foreach $varnames $valueLists $body
}
The root cause of the problem is (using your example) invoking proc a, which expects a list, with only a single scalar value when it is called in proc b. Your "workaround" of invoking a as, a [list $o], is the solution. It converts a single value into a list of one element. A list containing one element is not the same as a single value. Since lists in Tcl are just specially formatted strings, if the argument to proc a contains whitespace it will be treated as a list, split up into the whitespace separated components. Although Tcl is flexible enough to allow you to radically redefine the language, I think this is a case of "just because you can, doesn't mean you should." I just don't think this case is compelling enough since some code refactoring would make the problem disappear.
Ultimately, the issue is that you don't want to treat simple values as lists. One of the ways of dealing with that is indeed to use [list $a] to make a list out of the value that shouldn't be mistreated, but another is to change the a procedure to take multiple arguments so that you can treat them as a list internally while having quoting automatically applied:
# The args argument variable is special
proc a {args} {
puts "logging [llength $args] objects:"
foreach o $args {
puts "$o has column index [get_property COLUMN_INDEX $o]"
}
}
Then you can call it like this:
a $o
When passing in a list to a procedure like this, you want to use expansion syntax:
a {*}[get_clock_regions X0Y0]
That leading {*} is a pseudo-operator in Tcl, which means to interpret the rest of the argument as a list and to pass the words in the list as their own arguments.

Ruby - method changes input variable value

I'm new to Ruby and I'm having trouble understanding what's happening in this method.
I make this call in a Rails controller -
#arr = SomeClass.find_max_option(params[:x], #pos, params[:y], some_var)
I'm trying to return the value to #arr, which happens successfully, but manipulations I make to #pos within that method are being brought back as well; the value of #pos changes when I'm only trying to get the value for #arr.
Here's more details on the method
#before going into the method
#pos = [a,b]
def self.find_max_option(x, pos, y, some_var)
pos.collect! { |element|
(element == b) ? [c,d] : element
}
end
#new value of pos = [a, [c,d]] which is fine for inside in this method
... #some calculations not relevant to this question, but pos gets used to generate some_array
return some_array
But when the method is finished and gets back to the controller, the value of #pos is now [a,[c,d]] as well.
What's going on here? I thought that pos would be treated separately from #pos and the value wouldn't carry back. As a workaround I just created a new local variable within that method, but I'd like to know what this is happening
#my workaround is to not modify the pos variable
pos_groomed = pos.collect { |element|
(element == b) ? [c,d] : element
}
end
Instead of using collect!, just use collect (without the !). So, rewrite your method as:
def self.find_max_option(x, pos, y, some_var)
pos.collect { |element|
(element == b) ? [c,d] : element
}
end
When using the ! version of collect, you are replacing each element with the value returned by the block. However, when using collect without !, a new array is created, and the object where collect is being called it doesn't get changed. See the docs:
collect! vs collect
Using ! at the end of a method name is a common practice in Ruby. This question is related and would be worth taking a look.
You are using the destructive version of collect.
Destructive methods change the object on which the method is called, while non-destructive methods return new objects.
Ruby developers tend to call these methods 'bang methods', because the convention is that destructive methods have the ! suffix.
pos.collect! # changes pos and returns pos
pos.collect # creates a new object
Your workaround only works because you use the non-destructive collect, while the original code uses collect!
pos.collect do |element|
(element == b) ? [c,d] : element
end
Should work just fine.
As to why the object changes outside of the method:
In ruby, when you pass an argument to a method, you are actually passing the reference to the object.
So passing an array into a method doesn't make a copy, but simply passes the reference to original array.
There is no way to 'pass by value' but you can create a copy yourself with dup or clone, if you really have to.

Best way to handle where clause in Lambda

I have the following lambda for a simple search page using MVC:
Name and PostedName are strings.
Results.where(a=>a.Name.Contains(PostedName)).ToList();
Thanks great when PostedName has a value (excellent filter), but when it is empty, I get bupkas (empty list).
I would ideally like my where clause to be ignored when empty string.
How can this be done?
Something ideally shorthand without ifs and elses and whatifs.
Thanks!
You can dynamically add the WHERE clause. Keep in mind that you're just building an expression tree with these clauses and it's not actually executed until, in this case, you call .ToList(). So you can do something like this:
var filteredResults = Results;
if (!string.IsNullOrWhitespace(PostedName))
filteredResults = filteredResults.Where(a => a.Contains(PostedName));
filteredResults = filteredResults.ToList();
Depending on the types you may need to explicitly declare a type for filteredResults in order for that to compile.
If you want something a little more in-line, this may do the trick:
Results.Where(a => string.IsNullOrWhitespace(PostedName) || a.Contains(PostedName)).ToList();
I think it's less clear on the intent, though. The benefit of the first example is also that you can add more filters following the same structure, basically dynamically adding more WHERE clauses for other filter fields as needed.
David's answer is correct, but if you want a shortcut you can create an extension method to simplify usage example (untested by me).
I would suggest:
Results.Where(a => a.Name.Contains((PostedName ?? "").Trim())).ToList();
"ThisIsAString".Contains("") returns true.
In the case PostedName is null, it will be changed to "".
If there is leading and/or trailing blanks characters in PostedName, then they will be removed.

Way to deep traverse a Groovy object with dot in string using GPath

The situation I have is that I'm querying MongoDB with a string for a field that is more than one level deep in the object hierarchy. This query must be a string. So for example I'm querying for something like this in Groovy:
def queryField = 'a.b.c' //this is variable and can be different every time
def result = mongodb.collection.findOne([queryField:5])
The problem no arises that in the result I want to find the value of the nested field. With GPath I could go one level deep and get a's value doing this
def aObj = result."a" //or result["a"]
However I want to go deeper than that by doing something like this:
def queryField = "a.b.c" //this can change every time and is not always 'a.b.c'
def cObj = result[queryField] //since field is variable, can't just assume result.a.b.c
This does not work in Groovy right now. There is a bug logged here, but I was wondering if there is a better work around to use for this scenario that is a bit cleaner than me parsing the string by splitting on the dot and then building the object traversal. Note that "a.b.c" is variable and unknown at runtime (e.g. it could be "a.b.d").
Based on the bug/thread it would appear there are some ambiguity problems with supporting a dotted property accessor. Based on the mailing list thread it would seem that evaluating the queryField string would be your best bet:
def result = [a: [b: [c: 42]]]
def queryString = 'a.b.c'
def evalResult = Eval.x(result, 'x.' + queryString)
assert evalResult == 42
Script on Groovy Web Console
The mailing list thread is a little old, so there's a new-ish (since at least 1.7.2) Eval class that can help out with running small snippets that don't have a large binding.
Otherwise, you can split the string and recursively do property evaluations on the object, effectively reproducing a subset of GPath traversal behavior.

Could I improve this method with duck typing?

Hopefully I haven't misunderstood the meaning of "duck typing", but from what I've read, it means that I should write code based on how an object responds to methods rather than what type/class it is.
Here's the code:
def convert_hash(hash)
if hash.keys.all? { |k| k.is_a?(Integer) }
return hash
elsif hash.keys.all? { |k| k.is_a?(Property) }
new_hash = {}
hash.each_pair {|k,v| new_hash[k.id] = v}
return new_hash
else
raise "Custom attribute keys should be ID's or Property objects"
end
end
What I want is to make sure that I end up with a hash where the keys are an integer representing the ID of an ActiveRecord object. I don't particularly enjoy having to iterate through the hash keys twice with all? to determine if I need to grab the ID's out.
Of course, I'll accept any other suggestions to improve this code as well :)
How you write this method should depend on whether you expect an exception to be thrown during the course of normal program execution. If you want a readable exception message because an end-user might see it, then throwing one manually makes sense. Otherwise, I'd just do something like this:
def convert(hash)
new_hash = {}
hash.each_pair { |k,v| new_hash[ k.is_a?(Integer) ? k : k.id ] = v }
return new_hash
end
This will accomplish exactly the same thing, and you'll still get an exception if an array key doesn't have an id field. Even better, this uses a little more duck typing because now anything that has an id field will be acceptable, which is better than explicitly checking for something being a Property. This makes your code more flexible, especially when unit testing.
We still have an explicit check for integer objects, but this kind of occasional special case is usually acceptable, especially when checking for built-in data types.
Duck typing is really just a nuanced version of polymorphism. In a statically typed language like Java you'd have to create an explicit interface that told the compiler all of the methods that a particular variable can accept. With a dynamic language like Ruby the interfaces still exist in an abstract sense, they're just implicit.
The problem is the fact that you're accepting two different data structures into one method. The way to make duck typing work is to require that all the objects that get passed to your method obey the same contract (i.e. it's always a hash of Integers to [Foo] objects.) The process of converting a hash with Property keys into the correct structure should be the job of the client code. That can be done very easily with a simple wrapper class or a conversion function consisting of just the body of your elseif clause.
Bottom line it's up to the guy calling the method to make sure his parameters all quack the way your method expects them to quack. If they don't, he's the one who need's to figure out how to make his turkey quack like a duck, not you.
What I want is to make sure that I end up with a hash where the keys are an integer representing the ID of an ActiveRecord object.
You should probably check for that when you're creating/inserting into the hash. You could try something like this:
h = {}
def h.put obj
self[obj.id]=obj
end
or maybe
h = {}
def h.[]= key, value
raise "hell" unless key == value.id
super
end

Resources