Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Groovy ArrayList getting confused for a String

I am seeing some confusing behavior from this code.

def fileArrayMeta  = newFile.readLines()
def file3 = fileArrayMeta.collect{ line -> line.split("\\s+") }
println file3[0]
println file3[0].getClass()

The expected result of this operation should be.

=> [, NOTE, *ENTRY, STATEMENT, *.]
=> class java.util.ArrayList

The actual result of this operation is.

=> [, NOTE, *ENTRY, STATEMENT, *.]
=> class [Ljava.lang.String;

Why is this happening?

println file3[0].removeAll("", null)

Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: [Ljava.lang.String;.removeAll() is applicable for argument types: (java.lang.String, null) values: [, null]

I assume this should work too, why not?

def clean4 = clean3.each{it.collect{ it != "" && it != null }}
    println clean4[0]
like image 727
kschmit90 Avatar asked Jan 15 '15 14:01

kschmit90


2 Answers

The split method on java.lang.String returns an array, not an ArrayList. You could use the method tokenize instead (although it doesn't take a regex that's not a problem for the simple case of splitting on whitespace), or convert the array to a list after splitting, using Arrays.asList or as List.

In Groovy split and tokenize both have defaults assuming whitespace as a delimiter, so the regex is not required, either line.split() as List or line.tokenize() is enough. Tabs and spaces both get handled and repetitions work similarly:

groovy:000> s = '  a\tb   c d '
===>   a        b   c d
groovy:000> s.tokenize()
===> [a, b, c, d]
groovy:000> s.split() as List
===> [a, b, c, d]

The output [Ljava.lang.String is a notation telling you you have an array of Strings. You can check if something is an array by calling isArray() on the class:

file3[0].class.isArray()

The MissingMethodException is saying you are calling removeAll on an array. You can check the methods available on an object:

groovy:000> a = s.split()
===> [a, b, c, d]
groovy:000> a.class.isArray()
===> true
groovy:000> a.class.methods
===> [public final void java.lang.Object.wait(long,int) throws java.lang.Interru
ptedException, public final native void java.lang.Object.wait(long) throws java.
lang.InterruptedException, public final void java.lang.Object.wait() throws java
.lang.InterruptedException, public boolean java.lang.Object.equals(java.lang.Obj
ect), public java.lang.String java.lang.Object.toString(), public native int jav
a.lang.Object.hashCode(), public final native java.lang.Class java.lang.Object.g
etClass(), public final native void java.lang.Object.notify(), public final nati
ve void java.lang.Object.notifyAll()]
groovy:000> a.class.methods*.toString().grep {it.contains('removeAll')}
===> []

And what do you expect the last example to do? Because the call to collect will return a list of booleans:

groovy> stuff=['asdf', 'zxcv', 'qwerty', '', 'zzz'] 
groovy> stuff.collect { it != '' && it != null } 

Result: [true, true, true, false, true]

The collect method makes a transformed copy of the collection it's called on. But each doesn't return the modified collection, it returns the original unmodified one, here's an example:

groovy> def mylist = ['asdf', 'zxcv', 'qwerty'] 
groovy> def foo = mylist.each {it.toUpperCase()} 
groovy> foo 

Result: [asdf, zxcv, qwerty]

Here the closure in the each has no effect on the contents of mylist or on foo, unlike if you used collect or mylist*.toUpperCase(), which returns the collection of entries created by executing the closure on each element of mylist.

It looks like you are confused about what collect does, and you're trying to use it to filter (which doesn't work). Here's an example of removing empty strings from a list of lists of strings:

groovy> def mylist = [['a', 'b', ''], ['', 'c', 'd'], ['e', '', 'f']] 
groovy> println mylist.collect {it.findAll()} 

[[a, b], [c, d], [e, f]]

The it that findAll is called on is a list of strings.

like image 74
Nathan Hughes Avatar answered Sep 22 '22 05:09

Nathan Hughes


you can also use line.split( /\s+/ ) as List

like image 36
injecteer Avatar answered Sep 22 '22 05:09

injecteer