views:

76

answers:

2

I need to convert a String representation of a nested List back to a nested List (of Strings) in Groovy / Java, e.g.

String myString = "[[one, two], [three, four]]"
List myList = isThereAnyMethodForThis(myString)

I know that there's the Groovy .split method for splitting Strings by comma for example and that I could use regular expressions to identify nested Lists between [ and ], but I just want to know if there's an existing method that can do this or if I have to write this code myself.

I guess the easiest thing would be a List constructor that takes the String representation as an argument, but I haven't found anything like this.

+1  A: 

I would parse this String manually. Each time you see a '[' create a new List, each time you see a ',' add an element to the list and each time you see a ']' return.

With a recursive method.

public int parseListString(String listString, int currentOffset, List list){
    while(currentOffset < listString.length()){
        if(listString.startsWith("[", currentOffset)){
            //If there is a [ we need a new List
            List newList = new ArrayList();
            currentOffset  = parseListString(listString, currentOffset+1, newList);
            list.add(newList);
        }else if(listString.startsWith("]", currentOffset){
            //If it's a ], then the list is ended
            return currentOffset+1;
        }else{
            //Here we have a string, parse it until next ',' or ']'
            int nextOffset = Math.min(listString.indexOf(',', currentOffset), listString.indexOf(']', currentOffset));
            String theString = listString.substring(int currentOffset, int nextOffset);
            list.add(theString);

            //increment currentOffset
            currentOffset = nextOffset;
        }
    }
    return currentOffset;
}
Colin Hebert
Thank you, this is similar to what I would have tried, but tim_yates solution seems even simpler - although I don't know which one performs better, but I guess it doesn't make a difference with today's hardware.
werner5471
+6  A: 

In Groovy, if your strings are delimited as such, you can do this:

String myString = "[['one', 'two'], ['three', 'four']]"
List myList = Eval.me(myString)

However, if they are not delimited like in your example, I think you need to start playing with the shell and a custom binding...

class StringToList extends Binding {
  def getVariable( String name ) {
    name
  }
  def toList( String list ) {
    new GroovyShell( this ).evaluate( list )
  }
}

String myString = "[[one, two], [three, four]]"
List myList = new StringToList().toList( myString )

Edit to explain things

The Binding in Groovy "Represents the variable bindings of a script which can be altered from outside the script object or created outside of a script and passed into it."

So here, we create a custom binding which returns the name of the variable when a variable is requested (think of it as setting the default value of any variable to the name of that variable).

We set this as being the Binding that the GroovyShell will use for evaluating variables, and then run the String representing our list through the Shell.

Each time the Shell encounters one, two, etc., it assumes it is a variable name, and goes looking for the value of that variable in the Binding. The binding simply returns the name of the variable, and that gets put into our list

Another edit... I found a shorter way

You can use Maps as Binding objects in Groovy, and you can use a withDefault closure to Maps so that when a key is missing, the result of this closure is returned as a default value for that key. An example can be found here

This means, we can cut the code down to:

String myString = "[[one, two], [three, four]]"
Map bindingMap = [:].withDefault { it }
List myList = new GroovyShell( bindingMap as Binding ).evaluate( myString )

As you can see, the Map (thanks to withDefault) returns the key that was passed to it if it is missing from the Map.

tim_yates
werner5471
Added a bit more explanation...hope it helps :-)
tim_yates