Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GROOVY-5403 Add grep to Iterator, to return a new filtered Iterator #40

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/main/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
Expand Up @@ -1555,6 +1555,72 @@ public static boolean any(Object self) {
return false;
}

public static class GreppedIterator<T> implements Iterator<T> {
private Iterator<T> delegate ;
private T next ;
private Object filter ;

public GreppedIterator( Iterator<T> delegate, Object filter ) {
this.delegate = delegate ;
this.filter = filter ;
this.next = loadNext() ;
}

private T loadNext() {
T localNext = null ;
MetaClass filterMetaClass = InvokerHelper.getMetaClass( filter ) ;
while( localNext == null && delegate.hasNext() ) {
T object = delegate.next() ;
if( DefaultTypeTransformation.castToBoolean( filterMetaClass.invokeMethod( filter, "isCase", object ) ) ) {
localNext = object ;
}
}
return localNext ;
}

public boolean hasNext() {
return this.next != null ;
}

public T next() {
T ret = this.next ;
this.next = loadNext() ;
return ret ;
}

public void remove() {
throw new UnsupportedOperationException() ;
}
}

/**
* Takes an Iterator and a filter, and returns a new Iterator which will return only those
* objects which match the given filter - calling the <code>{@link #isCase(java.lang.Object, java.lang.Object)}</code>
* method used by switch statements. This method can be used with different
* kinds of filters like regular expressions, classes, ranges etc. It should be noted that this
* method will advance the passed Iterator by at least one element, as it caches the next valid item internally.
* Example:
*
* <pre class="groovyTestCase">
* // Collect is required as grep and take both return an Iterator when called on an Iterator
* assert [ 'a', 'aa' ] == ['a', 'b', 'aa', 'bc', 3, 4.5].iterator().grep( ~/a+/ ).collect()
* assert [ 'aa' ] == ['a', 'b', 'aa', 'bc', 3, 4.5].iterator().grep( ~/../ ).take( 1 ).collect()
* assert [ 3, 4.5 ] == ['a', 'b', 'aa', 'bc', 3, 4.5].iterator().grep( Number ).collect()
* assert [ 'a', 'b' ] == ['a', 'b', 'aa', 'bc', 3, 4.5].iterator().grep{ it.toString().size() == 1 }.take( 2 ).collect()
*
* def a = 1
* def iter = [ hasNext:{ true }, next:{ a++ } ] as Iterator
* def oddIter = iter.grep { it % 2 }
* assert [ 1, 3, 5 ] == oddIter.take( 3 ).collect()
* </pre>
* @param self the Iterator we wish to filter.
* @param filter the filter to perform on the object (using the {@link #isCase(java.lang.Object, java.lang.Object)} method).
* @return a new Iterator which will just return items in the original that match the filter.
*/
public static <T> Iterator<T> grep( Iterator<T> self, Object filter ) {
return new GreppedIterator<T>( self, filter ) ;
}

/**
* Iterates over the collection of items which this Object represents and returns each item that matches
* the given filter - calling the <code>{@link #isCase(java.lang.Object, java.lang.Object)}</code>
Expand Down
13 changes: 13 additions & 0 deletions src/test/groovy/GroovyMethodsTest.groovy
Expand Up @@ -285,6 +285,19 @@ class GroovyMethodsTest extends GroovySwingTestCase {
assert answer == ["Bob"]
}

void testIteratorGrep() {
// Set our initial value
def a = 1
// Define an iterator around this value
def iter = [ hasNext:{ true }, next:{ a++ } ] as Iterator

// Define a new iterator that will just emit odd numbers
def oddIter = iter.grep { it % 2 }

// Check it's working
assert [ 1, 3, 5 ] == oddIter.take( 3 ).collect()
}

void testCollectionToList() {
def c = [1, 2, 3, 4, 5] // but it's a list
def l = c.toList()
Expand Down