Skip to content

Loading…

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

Closed
wants to merge 2 commits into from

2 participants

@timyates
Groovy programming language member

https://jira.codehaus.org/browse/GROOVY-5403

grep currently works on collections, but it would be nice to have it work on Iterators

We could then do lazy things like:

// 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()
@blackdrag
Groovy programming language member
@blackdrag blackdrag closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 10, 2012
  1. @timyates
  2. @timyates
This page is out of date. Refresh to see the latest.
View
66 src/main/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -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>
View
13 src/test/groovy/GroovyMethodsTest.groovy
@@ -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()
Something went wrong with that request. Please try again.