5

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]
kschmit90
  • 378
  • 2
  • 3
  • 13
  • `L[]` is a native array, which has no `removeAll`. and thou shalt not use each to collect!. – cfrick Jan 15 '15 at 15:57
  • if you want to have all non-empty/non-null elements, then just use `findAll`. e.g. `assert ['a',null,''].findAll() == ['a']`, as groovy-truth gets rid of them. `each` just returns the original list and your `collect` calls are no-ops. – cfrick Jan 15 '15 at 21:21

2 Answers2

9

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.

Community
  • 1
  • 1
Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • I am calling the split method on a string and it prints as an array, when I call getClass it returns that it is a string. – kschmit90 Jan 15 '15 at 14:20
  • 2
    @kschmit90: getClass returns something starting with [L, that means it's an array. your output in the question is telling you it's an array of type String. – Nathan Hughes Jan 15 '15 at 14:23
  • 1
    `[Ljava.lang.String` mean array of String. `[` symbol indicate this – talex Jan 15 '15 at 14:23
  • Okay this is making a little more sense, but once I get that [a, b, c, d] there are blank spots. When I try to run a method to remove blank items from an array it says that it can not be called due to what I assume is an array actually being a String. – kschmit90 Jan 15 '15 at 14:52
  • 1
    @kschmit90: can you add an example to the question? – Nathan Hughes Jan 15 '15 at 14:55
  • 1
    @kschmit90: it looks like you're trying to call an ArrayList method removeAll on an array, which doesn't have that method. – Nathan Hughes Jan 15 '15 at 14:57
  • 1
    @kschmit90: the example at the bottom of the question is incomplete (doesn't specify the contents of clean3) and doesn't specify what the error is. it also seems unrelated to the original question. – Nathan Hughes Jan 15 '15 at 15:14
2

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

injecteer
  • 20,038
  • 4
  • 45
  • 89