How can I declare a string array in Groovy? I am trying as below but it is throwing an error
def String[] osList = new String[]
No expression for the array constructor call at line:
What am i doing wrong?
First of: welcome to SO!
You have a few options for creating arrays in groovy.
But let's start with what you've done wrong.
def String[] osList = new String[]
You used both def and String[] here.
Def is an anonymous type, which means that groovy will figure out which type it is for you.
String[] is the declared type, so what groovy will see here is:
String[] String[] osList = new String[]
which obviously won't work.
Arrays however need a fixed size, which needs to be given as an argument to the creation of the Array:
Type[] arr = new Type[sizeOfArray]
in your case if you'd want to have 10 items in the array you would do:
String[] osList = new String[10]
if you do not know how many Strings you will have, use a List instead. An ArrayList will do for this in most cases:
List<String> osList = new ArrayList<>()
now you can add items by calling:
osList.add("hey!")
or using groovy's list-add operator:
osList << "hey!"
For further issues you should refer to groovy's official documentation and see if you can't find the solution yourself!
A simple way is
String[] osList = []
assert osList.class.array
assert 'java.lang.String[]' == osList.class.typeName
Another question is that this definition is rather useless. This is an immutable zero-lenght String[] and can be used only as a constant somewhere.
def arr = [] as String[]
or
String[] arr = [] as String[]
This should do it. You can test it and play around in here: https://groovyconsole.appspot.com/
Related
I have an dynamic html file that groovy is generated from. Part of this html template format is {routeId}{groovyMap} like so
USER_FORM[name:'Dean', user:randomFunction([item:'s', day:'Tuesday'])]
or something like
USER_FORM[name: 'Dean', user: user]
I made the first example more complex. Currently, I split on ':' and validate all the keys supplied. What I would like to do is take the groovy snippet and grab all the keys and validate
1. all keys are strings
2. validate the keys against some meta data I already have
I do not care about the values at all. Currently, I split on ':' but obviously that won't work for all cases. I am worried about other complex cases I may not be thinking about.
This is for a templating engine and I prefer to failfast if possible making it easier on the user when something is wrong.
I concur with others that you want to avoid parsing directly.
If you use GroovyShell, you can dope the input string with no-op methodMissing and propertyMissing handlers. In this way, even the complex example will work.
See code below, including test-cases (extracting map string from the "USER_FORMstr" format is left to the reader).
class KeyGenerator {
// these could be "final static". omitted for brevity
def shell = new GroovyShell()
def methodMissingHandler = "def methodMissing(String name, args) {}"
def propertyMissingHandler = "def propertyMissing(String name) {}"
def generateKeys(mapStr) {
def evalInput = "${methodMissingHandler} ; " +
"${propertyMissingHandler} ; " +
"${mapStr}"
def map = shell.evaluate(evalInput)
return map.keySet()
}
}
// ------- main
def keyGenerator = new KeyGenerator()
def expected = new HashSet()
expected << "name"
expected << "user"
def mapStr = "[name:'Dean', user:randomFunction([item:'s', day:'Tuesday'])]"
assert expected == keyGenerator.generateKeys(mapStr)
def mapStr2 = "[name: 'Dean', user: user]"
assert expected == keyGenerator.generateKeys(mapStr2)
If I got you right, you can use something like:
String val = "USER_FORM[name:'Dean', user:randomFunction([item:'s', day:'Tuesday'])]"
def res = []
val.eachMatch( /[\[,] ?(\w+):/ ){ res << it[ 1 ] }
assert '[name, user, item, day]' == res.toString()
all keys are strings
When using the literal syntax for creating a Map, i.e.
Map m = [foo: 'bar']
as opposed to
Map m = new HashMap()
m.put('foo', 'bar')
the keys are always strings, even if you have a variable in scope with the same name as the key. For example, in the following snippet, the key will be the string 'foo', not the integer 6
def foo = 6
Map m = [foo: 'bar']
The only way you can create a Map using the literal syntax with a key that is not a string is if you have a variable in scope with the same name as the key and you wrap the key name in parentheses. For example, in the following snippet, the key will be the integer 6, not the string 'foo'
def foo = 6
Map m = [(foo): 'bar']
Currently, I split on ':' but obviously that won't work for all cases. I am worried about other complex cases I may not be thinking about.
Parsing a map literal using regex/string splitting seems like a bad idea as you'll likely end up badly re-implementing the Groovy lexer. Something like the following seems a better option
def mapString = '[foo: "bar"]'
Map map = Eval.me(mapString)
// now you can process the map via the Map interface, e.g.
map.keySet().toList() == ['foo']
I know curly brackets are not used to initialize array in Groovy but I have noticed one peculiar thing.
Why groovy doesn't give compiler error when I initialize an array like this.
String emailAddress = "test#gmail.com";
String [] var = {emailAddress};
println var[0];
Output: com.test.examples.GroovyTest$_main_closure1#12e4860e
When I try to declare like this I get error:
String [] var = {"a","b"};
Can anybody explain this?
When you do:
String [] var = {emailAddress};
That creates a Closure that returns a String emailAddress, and then crams that closure into a String array (by calling toString() on it), as that's what you told it to do ;-)
So var equals ['ConsoleScript0$_run_closure1#60fd82c1'] (or similar, depending on where you're running things)
When you do:
String [] var = {"a","b"};
The right-hand side is not a valid Closure, so the script fails to parse.
What you want is:
String[] var = ['a', 'b']
Or:
String[] var = [emailAddress]
I'm trying to use the Groovy way of creating a TreeMap<String, List<Data>> with default values so I easily add data to a new list if the key isn't already present.
TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>) [:].withDefault { [] }
As you can see, I have the requirement to use a TreeMap and withDefault only returns a Map instance, so I need to cast.
When I attempt to add a new list to the map,
myData[newKey].add(newData)
myData[newKey] is null. However, if I change my Map initilization to remove the TreeMap cast (and change the type to just Map instead of TreeMap), myData[newKey].add(newData) works as expected.
What's the reasoning for this? Can I not use withDefault if I cast the map?
The problem isn't just about the cast. It also has to do with the declared type. The problem can be simplified to something like this:
def map1 = [:].withDefault { 0 }
TreeMap map2 = map1
When that is executed map1 is an instance of groovy.lang.MapWithDefault and map2 is an instance of java.util.TreeMap. They are 2 separate objects on the heap, not just 2 references pointing to the same object. map2 will not have any default behavior associated with it. It is as if you had done this:
def map1 = [:].withDefault { 0 }
TreeMap map2 = new TreeMap(map1)
That is what is happening with your code. The cast and the generics just makes it less clear with your code.
This:
TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>) [:].withDefault { [] }
Can be broken down to this:
def tmpMap = [:].withDefault { [] }
TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>)tmpMap
I hope that helps.
EDIT:
Another way to see the same thing happening is to do something like this:
Set names = new HashSet()
ArrayList namesList = names
When the second line executes a new ArrayList is created as if you had done ArrayList namesList = new ArrayList(names). That looks different than what you have in your code, but the same sort of thing is happening. You have a reference with a static type associated with it and are pointing that reference at an object of a different type and Groovy is creating an instance of your declared type. In this simple example above, that declared type is ArrayList. In your example that declared type is TreeMap<String, List<Data>>.
How can I have a string list in ActionScript-3 ? I don't want to just use an array and always check for type or be type vulnerable. If I could make arrays type specific that would help me. In C# I can do:
List<string> list = new List<string>();
list.Add("hello world");
string str = list[0];
I wished I could do more or less the same in AS-3.
You can use the Vector type, like this:
var list:Vector.<String> = new <String>[];
// or new Vector.<String>();
list.push("hello world");
var str:String = list[0];
Note that AS3's Vector class lacks some functionality found in C#'s List<T> like Remove(), and includes some functionality you would find in other classes of C# such as pop(), typically found in Stack<T>.
In AS3 you can use Vector datatype.
See More info on Vector data type
Vector class works the same way as you need.
See http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Vector.html.
this question feels like it would have been asked already, but I've not found anything so here goes...
I have constructor which is handed a string which is delimited. From that string I need to populate an object's instance variables. I can easily split the string by the delimited to give me an array of strings. I know I can simply iterate through the array and set my instance variables using ifs or a switch/case statement based on the current array index - however that just feels a bit nasty. Pseudo code:
String[] tokens = <from generic string tokenizer>;
for (int i = 0;i < tokens.length;i++) {
switch(i) {
case(0): instanceVariableA = tokens[i];
case(1): instanceVarliableB = tokens[i];
...
}
}
Does anyone have any ideas of how I do this better/nicer?
For what it's worth, I'm working in Java, but I guess this is language independant.
Uhm... "nasty" is in the way the constructor handles the parameters. If you can't change that then your code snippet is as good as it may be.
You could get rid of the for loop, though...
instanceVariableA = tokens[0];
instanceVariableB = tokens[1];
and then introduce constants (for readibilty):
instanceVariableA = tokens[VARIABLE_A_INDEX];
instanceVariableB = tokens[VARIABLE_B_INDEX];
NOTE: if you could change the string parameter syntax you could introduce a simple parser and, with a little bit of reflection, handle this thing in a slightly more elegant way:
String inputString = "instanceVariableA=some_stuff|instanceVariableB=some other stuff";
String[] tokens = inputString.split("|");
for (String token : tokens)
{
String[] elements = token.split("=");
String propertyName = tokens[0];
String propertyValue = tokens[1];
invokeSetter(this, propertyName, propertyValue); // TODO write method
}
Could you not use a "for-each" loop to eliminate much of the clutter?
I really think the way you are doing it is fine, and Manrico makes a good suggestion about using constants as well.
Another method would be to create a HashMap with integer keys and string values where the key is the index and the value is the name of the property. You could then use a simple loop and some reflection to set the properties. The reflection part might make this a bit slow, but in another language (say, PHP for example) this would be much cleaner.
just an untested idea,
keep the original token...
String[] tokens = <from generic string tokenizer>;
then create
int instanceVariableA = 0;
int instanceVariableB = 1;
if you need to use it, then just
tokens[instanceVariableA];
hence no more loops, no more VARIABLE_A_INDEX...
maybe JSON might help?
Python-specific solution:
Let's say params = ["instanceVariableA", "instanceVariableB"]. Then:
self.__dict__.update(dict(zip(params, tokens)))
should work; that's roughly equivalent to
for k,v in zip(params, tokens):
setAttr(self, k, v)
depending on the presence/absence of accessors.
In a non-dynamic language, you could accomplish the same effect building a mapping from strings to references/accessors of some kind.
(Also beware that zip stops when either list runs out.)